提问背景 Question:
还有这
if (typeof htmlString !== "string") {
return htmlString;
}
我以前没发现
JS防御性编程:为何HTML转义函数需要类型检查
为什么需要 typeof htmlString !== "string" 类型检查?
这是一个非常典型且优秀的**防御性编程(Defensive Programming)**实践。在 JavaScript 这种弱类型/动态类型语言中,这个判断至关重要,主要有以下几个原因:
1. 防止程序崩溃(运行时错误)
JavaScript 的 .replace() 方法是 String 实例上的方法。如果传入的参数不是字符串(例如 undefined, null, number, object 等),直接调用 .replace() 会立即抛出 TypeError。
// 如果没有类型检查:
escapeHtml(undefined); // 报错:Uncaught TypeError: Cannot read properties of undefined (reading 'replace')
一旦抛出未捕获的异常,可能会导致整个前端页面卡死,或者 Node.js 异步任务中断。加上这行代码后,即使传入了非预期类型,函数也能安全退出,不会使程序崩溃。
2. 应对不确定的后端或用户输入
在实际开发中,数据往往来自后端 API 或用户输入,我们无法100%保证拿到的字段一定是字符串:
- 接口字段可能因为数据库缺失而返回
null或直接是undefined。 - 原本应该是 HTML 字符串的字段,在某些异常情况下可能返回了数字(如错误码
500)或对象。 - 通过类型守卫,可以让这个公共工具函数具备极强的容错性。
更好的健壮性写法建议
虽然原样返回 htmlString 已经能防止崩溃,但在实际业务中,我们还可以根据需求进一步优化:
方案 A:安全强制转换(适合需要展示非字符串数据的场景)
如果你希望数字 123 也能被安全转义并以字符串 "123" 输出,而不是原样返回数字类型,可以先进行安全转换:
function escapeHtml(val) {
// 1. 如果是 null 或 undefined,返回空字符串,避免显示 "null" 字样
if (val === null || val === undefined) {
return "";
}
// 2. 强制转换为字符串类型(这样数字、布尔值也能安全转义)
const str = String(val);
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
方案 B:配合 TypeScript 进行双重保障
如果你在项目中使用 TypeScript,可以通过类型约束在编译阶段就拦截大部分错误,并配合运行时的防御性代码:
function escapeHtml(htmlString: string): string {
if (typeof htmlString !== "string") {
return ""; // 运行时防御性兜底
}
return htmlString
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
总结:
编写公共工具函数(Utility Functions)时,**“永远不要相信用户的输入”和“永远不要相信接口的数据格式”**是核心原则。加上类型校验,是区分“能跑的代码”和“企业级健壮代码”的重要分水岭。