ts中 ?? 和 || 区别

在JavaScript和TypeScript开发中,我们经常需要为变量设置默认值。??(空值合并运算符)和 ||(逻辑或运算符)是两个常用的选择,但它们的判断逻辑有着本质区别。本文将深入解析这两个运算符的区别,帮助你写出更严谨的代码。

核心区别速览

|| 会在左侧值为 任何假值(falsy) 时返回右侧值,而 ?? 仅在左侧值为 nullundefined 时才返回右侧值。

这是两者最根本的差异,也是理解它们行为的关键。

JavaScript 中的假值(falsy)完整列表

在深入之前,先了解 JavaScript 中哪些值被视为假值。JavaScript 中一共有 7 个假值

  • false
  • 0(包括 -00n
  • ""(空字符串)
  • null
  • undefined
  • NaN

除了这 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""falseNaN 这些假值出现时,|| 会错误地使用默认值,而 ?? 则会保留这些原本有意义的值。

下面通过具体代码来深入理解:

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;

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+ 还引入了逻辑赋值运算符,它们的行为与对应的二元运算符一致:

  • ||=:左侧为任何假值时赋值
  • ??=:左侧为 nullundefined 时才赋值
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);

总结与选择建议

  1. 核心记忆

    • ?? = 只认 nullundefined("空值")
    • || = 认所有假值(false0""nullundefinedNaN
  2. 简单原则 :当 0""false 在你的业务场景中是有意义的值时,优先使用 ??

  3. ESLint 建议 :许多现代项目配置了 @typescript-eslint/prefer-nullish-coalescing 规则,鼓励使用 ?? 替代 || 来处理空值,因为它能更精确地表达意图,避免因假值判断而引入 bug。

  4. 团队协作 :在项目中统一使用 ?? 处理空值可以提升代码可读性,降低因假值判断不一致而引入的潜在风险。

相关推荐
冴羽2 小时前
请愿书:Node.js 核心代码不应该包含 AI 代码!
前端·javascript·node.js
我家猫叫佩奇2 小时前
一款灵感源自《集合啦!动物森友会》的 UI 组件库
前端
mmmmm123422 小时前
深入 DOM 查询底层:HTMLCollection 动态原理与 querySelectorAll 静态快照解析
前端·javascript
weixin199701080162 小时前
《TikTok 商品详情页前端性能优化实战》
前端·性能优化
闲坐含香咀翠2 小时前
告别二次登录!Web端检测并唤起Electron客户端实战
前端·客户端
岁月宁静2 小时前
都知道AI大模型能生成文本内容,那你知道大模型是怎样生成文本的吗?
前端·vue.js·人工智能
花间相见3 小时前
【终端效率工具01】—— Yazi:Rust 编写的现代化终端文件管理器,告别繁琐操作
前端·ide·git·rust·极限编程
|晴 天|3 小时前
我如何用Vue 3打造一个现代化个人博客系统(性能提升52%)
前端·javascript·vue.js
风止何安啊3 小时前
网页都知道要双向握手才加载!从 URL 到页面渲染,单向喜欢连 DNS 都解析不通
前端·javascript·面试