【JavaScript 】从 || 到??:JavaScript 空值处理的最佳实践升级

在前端开发中,"给变量设置默认值""处理空值兜底" 是高频场景

早期我们常用 ||(逻辑或)实现,但它常会把 0、空字符串 ''false 这类 "合法假值" 误判为 "空值",导致业务逻辑出错

ES2020 推出的 ??(空值合并

一、?? 是什么?核心规则一眼懂

?? 全称 "Nullish Coalescing Operator(空值合并运算符)",是 ES2020 新增的语法,核心作用是:仅当左侧操作数为 nullundefined 时,才返回右侧的默认值;否则返回左侧值

运算符)完美解决了这个问题,成为处理空值的最佳实践。

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 特性之一。

  1. **不能直接和 && / || 混用(不加括号)**JS 不允许无括号的混合运算,会抛出语法错误,需用括号明确优先级:

    javascript 复制代码
    // 错误写法
    const result = a && b ?? c;
    // 正确写法
    const result = (a && b) ?? c;
  2. 浏览器兼容性 ?? 是 ES2020 特性,现代浏览器(Chrome 80+/Firefox 72+/Edge 80+)已支持,如需兼容低版本,可通过 Babel 转译(配置 @babel/plugin-proposal-nullish-coalescing-operator)。

  3. 不要过度使用 仅当需要 "区分 null/undefined 和其他假值" 时使用 ??;如果需要判断所有假值(比如 "空值就显示默认"),用 || 更合适。

  4. 嵌套属性访问 → 结合 ?. + ??,安全又精准。

相关推荐
belldeep2 小时前
python:用 Flask 3 , mistune 2 和 mermaid.min.js 10.9 来实现 Markdown 中 mermaid 图表的渲染
javascript·python·flask
m0_736919102 小时前
C++中的委托构造函数
开发语言·c++·算法
lsx2024062 小时前
Python3 SMTP发送邮件教程
开发语言
懈尘2 小时前
从 Java 1.7 到 Java 21:逐版本深入解析新特性与平台演进
java·开发语言
凉辰2 小时前
使用uni.createInnerAudioContext()播放指定音频(踩坑分享功能)
开发语言·javascript·音视频
hello 早上好2 小时前
05_Java 类加载过程
java·开发语言
PPPPPaPeR.2 小时前
光学算法实战:深度解析镜片厚度对前后表面折射/反射的影响(纯Python实现)
开发语言·python·数码相机·算法
echoVic2 小时前
多模型支持的架构设计:如何集成 10+ AI 模型
java·javascript
橙露2 小时前
Java并发编程进阶:线程池原理、参数配置与死锁避免实战
java·开发语言