在 JavaScript 开发过程中,为变量设置默认值是再常见不过的操作。然而,长期以来习惯使用的逻辑或操作符(||),在某些特定场景下却隐藏着足以引发逻辑崩溃的陷阱。
前排广告位:欢迎访问我的个人网站 hixiaohezi.com
欢迎关注微信公众号“Hi 小禾子”
随着 ES6 引入了空值合并操作符(??),开发者终于拥有了更精确的方式来处理“空值”。本文将深度解析这两者的本质区别,并探讨在实战中如何规避潜在风险。
一、 典型案例:消失的数字 0
考虑这样一个分页组件的逻辑:
// 假设从 URL 参数或 API 中获取配置
const config = {
pageSize: 0, // 用户显式设置每页显示 0 条(可能用于某些特殊逻辑)
theme: "" // 显式设置为空字符串
};
// 使用逻辑或设置默认值
const pageSize = config.pageSize || 10;
const theme = config.theme || "dark";
console.log(pageSize); // 输出: 10(预期外)
console.log(theme); // 输出: "dark"(预期外)
在此案例中,原本期望保留显式设置的 0 或空字符串 ""。但由于 || 操作符的短路逻辑,这些合法的业务数值被无情地覆盖了。
二、 追根溯源:Falsy (虚值) 的代价
逻辑或操作符(||)的本质是:返回第一个真值(Truthy) 。
在 JavaScript 中,以下值都被视为“虚值”(Falsy):
false0""(空字符串)nullundefinedNaN
当使用 value || defaultValue 时,只要 value 属于上述任何一种情况,结果都会指向 defaultValue。在处理 UI 状态、计数器或表单配置时,这种“广撒网”式的判断往往会导致逻辑不符合预期。
三、 正解方案:空值合并操作符 ??
ES2020 引入的 ?? 操作符提供了一种更严谨的选择。它的判断逻辑非常明确:只有当左侧操作数为 null 或 undefined 时,才会返回右侧的操作数。
对于 0、"" 或 false,?? 会将其视为有效值并保留。
性能对比表
| 输入值 (input) | input || "default" | input ?? "default" | 结论 | | :--- | :--- | :--- | :--- | | undefined | "default" | "default" | 一致 | | null | "default" | "default" | 一致 | | 0 | "default" | 0 | 差异点 | | "" | "default" | "" | 差异点 | | false | "default" | false | 差异点 |
四、 进阶陷阱:运算符优先级问题
在使用 ?? 时,有一个容易被忽略的语法限制:出于安全考虑,?? 不能在不使用括号的情况下直接与 && 或 || 混用。
// 语法错误:Uncaught SyntaxError
const result = a || b ?? c;
// 正确写法:必须明确优先级
const result = (a || b) ?? c;
这种设计强制开发者显式声明逻辑结构,从而避免代码在极端边界条件下产生歧义。
五、 逻辑赋值:??= 的妙用
为了进一步简化代码,ES2021 推出了逻辑赋值操作符。这在处理深层配置初始化时非常优雅:
const settings = {
timeout: 0
};
// 只有当 timeout 为空时才赋值
settings.timeout ??= 3000;
console.log(settings.timeout); // 依然输出: 0
六、 总结
逻辑或 || 本质上是针对“虚值”的兜底,而空值合并 ?? 则是针对“存在性”的检查。
在绝大多数需要设置默认值的业务场景中,?? 往往是更安全的选择。通过更精确地表达逻辑意图,可以显著减少由于 JS 类型隐式转换引发的隐形 Bug。
欢迎访问我的个人网站 hixiaohezi.com
欢迎关注微信公众号“Hi 小禾子”