在前端开发中,"给变量设置默认值""处理空值兜底" 是高频场景
早期我们常用 ||(逻辑或)实现,但它常会把 0、空字符串 ''、false 这类 "合法假值" 误判为 "空值",导致业务逻辑出错
ES2020 推出的 ??(空值合并
一、?? 是什么?核心规则一眼懂
?? 全称 "Nullish Coalescing Operator(空值合并运算符)",是 ES2020 新增的语法,核心作用是:仅当左侧操作数为 null 或 undefined 时,才返回右侧的默认值;否则返回左侧值。
运算符)完美解决了这个问题,成为处理空值的最佳实践。
javascript
const result = 左侧值 ?? 右侧默认值;
|---------------|-------|---------|
| 左侧值 | 运算结果 | 说明 |
| null | 右侧默认值 | 仅空值触发兜底 |
| undefined | 右侧默认值 | 仅空值触发兜底 |
| 0 | 0 | 保留合法假值 |
| ''(空字符串) | '' | 保留合法假值 |
| false | false | 保留合法假值 |
| 123/'abc' | 左侧值本身 | 非空值直接返回 |
[核心判定规则]
二、?? vs ||(为什么说 || 容易踩坑?)
新手最容易混淆 ?? 和 ||,但二者的判定逻辑差异极大 ------|| 基于 "布尔真假" 判断,?? 基于 "是否为空值(null/undefined)" 判断,这也是 || 频繁出 bug 的核心原因。
实战对比:业务场景中的坑
假设我们做一个 "商品数量展示" 功能,用户输入数量为 0 时,需要显示 0 而非默认值,用 || 就会出错:
javascript
// 业务需求:仅当用户未输入数量(null/undefined)时,默认显示 1
const userInput = 0; // 用户明确输入0件商品
// 错误写法:|| 把 0 判定为假值,返回默认值 1
const countBad = userInput || 1;
console.log(countBad); // 输出 1(不符合预期)
// 正确写法:?? 仅判断 null/undefined,保留 0
const countGood = userInput ?? 1;
console.log(countGood); // 输出 0(符合预期)
再比如 "用户昵称" 场景,用户可能设置空字符串作为昵称,|| 会错误覆盖:
javascript
const userName = ''; // 用户主动设置空昵称
const nameBad = userName || '未知用户'; // 输出 '未知用户'(错误)
const nameGood = userName ?? '未知用户'; // 输出 ''(正确)
|-------|--------------------|------------------------|
| 特性 | ?? 运算符 | || 运算符 |
| 判断依据 | 是否为 null/undefined | 是否为 "假值"(0/''/false 等) |
| 适用场景 | 精准空值兜底 | 所有假值兜底 |
| 业务安全性 | 高(不覆盖合法假值) | 低(易误判合法假值) |
[核心区别总结]
三、?? 的高频实战场景
?? 不是单纯的 "语法糖",而是精准解决业务问题的工具,以下是前端开发中最常用的 3 个场景:
场景 1:嵌套属性安全访问 + 兜底(结合可选链?.)
开发中常需要访问 obj.a.b.c 这类嵌套属性,直接访问易报错,结合 ?. 和 ?? 可实现 "安全访问 + 兜底":
javascript
// 需求:获取用户年龄,无则默认 18
const user = { info: { age: 0 } }; // 用户年龄为 0(合法值)
// 安全写法:?. 避免层级不存在报错,?? 仅兜底 null/undefined
const age = user?.info?.age ?? 18;
console.log(age); // 输出 0(保留合法值)
// 如果 user 无 info 属性:
const user2 = {};
const age2 = user2?.info?.age ?? 18;
console.log(age2); // 输出 18(兜底生效)
场景 2:多优先级兜底(链式??)
业务中常需要 "优先取 A,无则取 B,再无则取默认值",链式 ?? 可简洁实现:
javascript
// 需求:门店功能项优先取状态2,无则取1,再无则返回默认值
const getTarget = (store) => {
return store.storeFun?.find(vo => vo.storeStatus === 2)
?? store.storeFun?.find(vo => vo.storeStatus === 1)
?? { storeCode: '默认门店' };
};
// 测试:有2则返回2
const store1 = { storeFun: [{ storeStatus: 2, storeCode: 'code2' }] };
console.log(getTarget(store1).storeCode); // 输出 code20
// 测试:无2有1则返回1
const store2 = { storeFun: [{ storeStatus: 1, storeCode: 'code1' }] };
console.log(getTarget(store2).storeCode); // 输出 code1
// 测试:无2无1则返回默认
const store3 = { storeFun: [] };
console.log(getTarget(store3).storeCode); // 输出 默认门店
场景 3:函数参数默认值(替代 ES6 参数默认值的局限)
ES6 函数参数默认值仅在参数为 undefined 时生效,但结合 ?? 可更灵活:
javascript
// 需求:函数参数为 null/undefined 时用默认值,0/'' 保留
function calculate(price = 100) {
// 传统参数默认值:仅 undefined 生效,null 会被保留
console.log(price);
}
calculate(null); // 输出 null(不符合预期)
// 优化:用 ?? 兜底 null/undefined
function calculateBetter(price) {
const finalPrice = price ?? 100;
console.log(finalPrice);
}
calculateBetter(null); // 输出 100(符合预期)
calculateBetter(0); // 输出 0(保留合法值)
四、使用??的注意事项
五、总结
?? 运算符的出现,填补了 JavaScript 精准处理 "空值" 的空白 ------ 它既解决了 || 误判合法假值的问题,又比手动判断 null/undefined 更简洁。在实际开发中,推荐遵循以下原则:
需精准兜底(仅 null/undefined)→ 用 ??;
需兜底所有假值 → 用 ||;
掌握 ?? 不仅能减少业务 bug,还能让代码更简洁、语义更清晰,是前端开发者必备的 ES2020 特性之一。
-
**不能直接和 && / || 混用(不加括号)**JS 不允许无括号的混合运算,会抛出语法错误,需用括号明确优先级:
javascript// 错误写法 const result = a && b ?? c; // 正确写法 const result = (a && b) ?? c; -
浏览器兼容性
??是 ES2020 特性,现代浏览器(Chrome 80+/Firefox 72+/Edge 80+)已支持,如需兼容低版本,可通过 Babel 转译(配置@babel/plugin-proposal-nullish-coalescing-operator)。 -
不要过度使用 仅当需要 "区分 null/undefined 和其他假值" 时使用
??;如果需要判断所有假值(比如 "空值就显示默认"),用||更合适。 -
嵌套属性访问 → 结合
?.+??,安全又精准。