跳到主要内容

JavaScript Nullish Coalescing

什么是 Nullish Coalescing?

Nullish Coalescing(空值合并)运算符(写作 ??)是 JavaScript ES2020 (ES11) 引入的一个逻辑运算符,专门用于处理 nullundefined 值。当我们需要为可能是 nullundefined 的变量提供默认值时,这个运算符特别有用。

备注

空值合并运算符 ?? 是 JavaScript 中比较新的特性,2020年才被正式纳入 ECMAScript 标准。

基本语法

js
leftExpr ?? rightExpr

如果左侧表达式 (leftExpr) 的值不是 nullundefined,整个表达式将返回左侧值;否则返回右侧表达式 (rightExpr) 的值。

与逻辑或 (||) 运算符的区别

在 JavaScript 中,许多开发者习惯使用逻辑或运算符 (||) 来设置默认值:

js
const name = userName || 'Guest';

|| 运算符会在左侧值为任何"假值"(如 0、空字符串 ''falseNaNnullundefined)时返回右侧值。这在某些情况下可能导致意外结果。

与之相对,?? 只在左侧值为 nullundefined 时才会返回右侧值,这在处理其他假值(如 0 或空字符串)但它们是有效输入时特别有用。

示例讲解

基础示例

js
// 使用 ?? 运算符
let score = null;
let displayScore = score ?? 'Not available';
console.log(displayScore); // 输出: "Not available"

score = 0;
displayScore = score ?? 'Not available';
console.log(displayScore); // 输出: 0 (注意这里!)

// 对比 || 运算符
score = 0;
displayScore = score || 'Not available';
console.log(displayScore); // 输出: "Not available" (可能不是期望的结果)

深入理解

让我们看看不同值的表现:

js
// null 和 undefined 将使用默认值
console.log(null ?? 'default'); // "default"
console.log(undefined ?? 'default'); // "default"

// 非 null/undefined 值将被保留,即使是假值
console.log(0 ?? 'default'); // 0
console.log('' ?? 'default'); // ""
console.log(false ?? 'default'); // false
console.log(NaN ?? 'default'); // NaN

链式使用

?? 运算符可以链式使用,返回第一个不是 nullundefined 的值:

js
const firstChoice = null;
const secondChoice = undefined;
const thirdChoice = 'Available';
const fallback = 'Default';

const result = firstChoice ?? secondChoice ?? thirdChoice ?? fallback;
console.log(result); // 输出: "Available"

使用场景

1. 用户配置与默认值

当处理用户配置或首选项时,?? 运算符非常有用:

js
function initializeApp(config) {
const port = config.port ?? 3000;
const theme = config.theme ?? 'light';
const timeout = config.timeout ?? 5000;

console.log(`Starting server on port ${port} with ${theme} theme`);
// ...其他初始化代码
}

// 即使传入 port: 0 (有效端口),也能正确处理
initializeApp({ port: 0, theme: null });
// 输出: "Starting server on port 0 with light theme"

2. API 响应处理

当从 API 获取数据时,某些字段可能是 null

js
const response = {
user: {
name: 'Alice',
age: null, // 用户没有提供年龄
address: undefined // 地址信息缺失
}
};

const userName = response.user.name ?? 'Anonymous';
const userAge = response.user.age ?? 'Not specified';
const userAddress = response.user.address ?? 'No address provided';

console.log(`User: ${userName}, Age: ${userAge}, Address: ${userAddress}`);
// 输出: "User: Alice, Age: Not specified, Address: No address provided"

3. 表单输入处理

js
function processFormData(formData) {
// 即使用户输入了 0 或空字符串,也应该被视为有效输入
const quantity = formData.quantity ?? 1;
const notes = formData.notes ?? 'No additional notes';

return {
processedQuantity: quantity,
processedNotes: notes
};
}

console.log(processFormData({ quantity: 0, notes: '' }));
// 输出: { processedQuantity: 0, processedNotes: "" }

console.log(processFormData({}));
// 输出: { processedQuantity: 1, processedNotes: "No additional notes" }

注意事项

运算符优先级

?? 运算符具有相对较低的优先级,因此在复杂表达式中可能需要使用括号:

js
// 没有括号,会先计算 null || 'foo'
const result1 = null || 'foo' ?? 'bar'; // SyntaxError

// 正确使用括号明确优先级
const result2 = (null || 'foo') ?? 'bar'; // "foo"
注意

出于安全考虑,将 ?? 直接与 AND(&&)和 OR(||)运算符一起使用是不允许的,会导致语法错误。必须使用括号明确表达你的意图。

短路求值

&&|| 类似,?? 也具有短路求值的特性。如果左侧表达式不为 nullundefined,右侧表达式将不会被求值:

js
function getDefaultValue() {
console.log('Generating default value');
return 'default';
}

const value = 'actual' ?? getDefaultValue();
// "Generating default value" 不会被输出,因为左侧值不是 null 或 undefined

实际应用案例

用户个人资料展示

js
function displayUserProfile(profile) {
const displayName = profile.nickname ?? profile.username ?? profile.email ?? 'Anonymous User';
const bio = profile.bio ?? 'This user has not added a bio yet.';
const location = profile.city ?? profile.country ?? 'Location unknown';

return `
<div class="profile">
<h2>${displayName}</h2>
<p>${bio}</p>
<span>${location}</span>
</div>
`;
}

// 实际使用
const incompleteProfile = {
email: 'user@example.com',
country: 'Canada',
bio: '' // 空字符串是有效值,不应替换为默认值
};

console.log(displayUserProfile(incompleteProfile));
// 使用邮箱作为显示名,保留空字符串作为简介,使用国家作为位置

处理环境变量

js
// 在Node.js应用中读取配置
function getConfig() {
return {
databaseUrl: process.env.DATABASE_URL ?? 'mongodb://localhost:27017',
port: Number(process.env.PORT ?? 3000),
environment: process.env.NODE_ENV ?? 'development',
debug: process.env.DEBUG === 'true' ?? false
};
}

const config = getConfig();
console.log(`Starting server in ${config.environment} mode on port ${config.port}`);

浏览器兼容性

空值合并运算符是比较新的特性,在一些旧浏览器中可能不支持。如果需要兼容旧浏览器,可以使用如下方式模拟:

js
// ES2020 空值合并运算符
const result = value ?? defaultValue;

// 旧浏览器兼容方式
const result = (value !== null && value !== undefined) ? value : defaultValue;

或使用 Babel 等转译工具处理。

总结

空值合并运算符 ?? 提供了一种简洁、明确的方式来处理 nullundefined 值,同时保留其他假值。与逻辑或运算符 || 相比,它更加精确,尤其适用于:

  • 在不丢失有效假值(如 0'')的情况下设置默认值
  • 处理可能缺失的数据或配置选项
  • 构建条件回退链,选择第一个存在的值

通过合理使用空值合并运算符,你可以使代码更简洁、更可预测,并且避免许多与默认值相关的常见陷阱。

练习

  1. 编写一个函数,接受用户对象,并返回格式化的用户信息,使用 ?? 处理缺失的字段。
  2. 创建一个表单验证函数,使用 ?? 运算符为空输入提供默认值。
  3. 比较 ??|| 在处理以下值时的不同表现:0, '', false, null, undefined

额外资源

通过掌握空值合并运算符,你将能更精确地控制默认值的行为,提高代码质量和可维护性。