【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. 嵌套属性访问 → 结合 ?. + ??,安全又精准。

相关推荐
m0_459252466 分钟前
fastadmin动态渲染统计信息
开发语言·前端·javascript·php
该怎么办呢17 分钟前
Source/Core/Matrix4.js
前端·javascript
傻啦嘿哟24 分钟前
Python 操作 Excel 条件格式指南
开发语言·python·excel
逆境不可逃25 分钟前
LeetCode 热题 100 之 33. 搜索旋转排序数组 153. 寻找旋转排序数组中的最小值 4. 寻找两个正序数组的中位数
java·开发语言·数据结构·算法·leetcode·职场和发展
星空下的月光影子39 分钟前
易语言开发从入门到精通:进阶篇·数据处理与分析自动化·高频刚需手工转自动场景全覆盖
开发语言
林夕sama41 分钟前
多线程基础(四)
java·开发语言
Yang-Never44 分钟前
ADB ->adb shell perfetto 抓取 trace 指令
android·开发语言·adb·android studio
小鸡吃米…1 小时前
Python 网络爬虫 —— 环境设置
开发语言·爬虫·python
add45a1 小时前
C++中的观察者模式
开发语言·c++·算法