在JavaScript和TypeScript开发中,我们经常需要为变量设置默认值。??(空值合并运算符)和 ||(逻辑或运算符)是两个常用的选择,但它们的判断逻辑有着本质区别。本文将深入解析这两个运算符的区别,帮助你写出更严谨的代码。
核心区别速览
|| 会在左侧值为 任何假值(falsy) 时返回右侧值,而 ?? 仅在左侧值为 null 或 undefined 时才返回右侧值。
这是两者最根本的差异,也是理解它们行为的关键。
JavaScript 中的假值(falsy)完整列表
在深入之前,先了解 JavaScript 中哪些值被视为假值。JavaScript 中一共有 7 个假值:
false0(包括-0和0n)""(空字符串)nullundefinedNaN
除了这 7 个值以外,其他所有值在布尔上下文中都被视为真值(truthy)。
核心对比:数值、字符串与布尔值场景
下面的对比表格清晰地展示了 ?? 和 || 在不同输入值下的行为差异:
| 输入值 a | a ?? "default" | a || "default" |
|-----------|-----------------|-----------------|
| null | "default" | "default" |
| undefined | "default" | "default" |
| 0 | 0 | "default" |
| "" | "" | "default" |
| false | false | "default" |
| NaN | NaN | "default" |
| "hello" 等真值 | "hello" | "hello" |
可以看到,当 0、""、false 和 NaN 这些假值出现时,|| 会错误地使用默认值,而 ?? 则会保留这些原本有意义的值。
下面通过具体代码来深入理解:
1. 处理数字 0------最常见的坑
typescript
// 用户可能真的想设置音量/页码为 0
const userVolume = 0;
// ❌ 使用 ||:0 被当作无效值
const volume1 = userVolume || 50; // 结果:50(用户音量被覆盖!)
// ✅ 使用 ??:0 被正确保留
const volume2 = userVolume ?? 50; // 结果:0(符合预期)
在使用 || 时,0 被视为假值,因此返回了默认值 50,这可能完全违背了用户的意图------比如用户想静音或跳转到第 0 页。
2. 处理空字符串
typescript
const userInput = "";
// ❌ 使用 ||:空字符串被当作无效输入
const name1 = userInput || "匿名用户"; // 结果:"匿名用户"
// ✅ 使用 ??:空字符串被正确保留
const name2 = userInput ?? "匿名用户"; // 结果:""
空字符串可能代表用户主动清空了输入框,使用 || 会错误地将其替换为默认值。
3. 处理布尔值 false
typescript
const isEnabled = false;
// ❌ 使用 ||:false 被当作无效值
const flag1 = isEnabled || true; // 结果:true(覆盖了 false)
// ✅ 使用 ??:false 被正确保留
const flag2 = isEnabled ?? true; // 结果:false(符合预期)
false 本身是一个有效的布尔值,在使用 || 提供默认值时会被错误覆盖。
4. 处理 NaN
typescript
const calculatedValue = NaN;
// ❌ 使用 ||:NaN 被当作无效值
const value1 = calculatedValue || 100; // 结果:100
// ✅ 使用 ??:NaN 被正确保留
const value2 = calculatedValue ?? 100; // 结果:NaN
实际开发场景:该用哪个?
场景一:分页/计数类数值------推荐 ??
typescript
// 当前页码为 0(第 1 页)是有效值
const currentPage = requestParams.page ?? 1;
// 评论数量为 0 是有效值
const commentCount = apiResponse.comments ?? 0;
场景二:表单输入处理------推荐 ??
typescript
// 温度传感器可能返回 0℃------这是有效读数
const temperature = sensorValue ?? 20;
// 字体大小可能为 0(表示最小字号)
const fontSize = userSettings.fontSize ?? 16;
场景三:字符串空值判断------考虑 ||
typescript
// 用户名输入:空字符串应显示为"匿名用户"
const displayName = username || "匿名用户";
// 或者显式处理三种情况
const displayName = username?.trim() || "匿名用户";
当空字符串和 null/undefined 都应被视为无效时,|| 是合适的选择。
场景四:布尔值开关------推荐 ??
typescript
// 用户偏好设置:false 表示"已禁用",不应被默认值覆盖
const darkMode = userPreference.darkMode ?? true;
场景五:Vue 模板中的使用
vue
<template>
<!-- 留言内容:空字符串显示占位文案 -->
<div>{{ message || '暂无留言' }}</div>
<!-- 评分:0 分是有效分数 -->
<div>评分:{{ rating ?? '未评分' }}</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const message = ref('');
const rating = ref(0);
</script>
进阶篇:??= 和 ||= 的区别
TypeScript 4.0+ 还引入了逻辑赋值运算符,它们的行为与对应的二元运算符一致:
||=:左侧为任何假值时赋值??=:左侧为null或undefined时才赋值
typescript
let config1 = { volume: 0 };
let config2 = { volume: 0 };
config1.volume ||= 50; // volume 变成 50(0 被覆盖)
config2.volume ??= 50; // volume 保持 0(0 被保留)
兼容性与注意事项
版本要求
?? 是 ES2020(ES11) 引入的新特性,在 TypeScript 3.7+ 中可用。
现代浏览器(Chrome 80+、Firefox 72+、Safari 13.1+)和 Node.js 14+ 均已支持。对于需要兼容旧环境的项目,可以通过 Babel 等工具进行转译。
语法限制:不能与 && 或 || 直接混用
出于语法歧义的考虑,ES 规范不允许 ?? 与 && 或 || 直接组合使用,否则会抛出语法错误:
typescript
// ❌ 语法错误!
a || b ?? c;
a ?? b || c;
// ✅ 正确写法:使用括号明确优先级
(a || b) ?? c;
a ?? (b || c);
总结与选择建议
-
核心记忆:
??= 只认null和undefined("空值")||= 认所有假值(false、0、""、null、undefined、NaN)
-
简单原则 :当
0、""、false在你的业务场景中是有意义的值时,优先使用??。 -
ESLint 建议 :许多现代项目配置了
@typescript-eslint/prefer-nullish-coalescing规则,鼓励使用??替代||来处理空值,因为它能更精确地表达意图,避免因假值判断而引入 bug。 -
团队协作 :在项目中统一使用
??处理空值可以提升代码可读性,降低因假值判断不一致而引入的潜在风险。