JavaScript Nullish Coalescing
什么是 Nullish Coalescing?
Nullish Coalescing(空值合并)运算符(写作 ??
)是 JavaScript ES2020 (ES11) 引入的一个逻辑运算符,专门用于处理 null
或 undefined
值。当我们需要为可能是 null
或 undefined
的变量提供默认值时,这个运算符特别有用。
空值合并运算符 ??
是 JavaScript 中比较新的特性,2020年才被正式纳入 ECMAScript 标准。
基本语法
leftExpr ?? rightExpr
如果左侧表达式 (leftExpr
) 的值不是 null
或 undefined
,整个表达式将返回左侧值;否则返回右侧表达式 (rightExpr
) 的值。
与逻辑或 (||) 运算符的区别
在 JavaScript 中,许多开发者习惯使用逻辑或运算符 (||
) 来设置默认值:
const name = userName || 'Guest';
但 ||
运算符会在左侧值为任何"假值"(如 0
、空字符串 ''
、false
、NaN
、null
或 undefined
)时返回右侧值。这在某些情况下可能导致意外结果。
与之相对,??
只在左侧值为 null
或 undefined
时才会返回右侧值,这在处理其他假值(如 0
或空字符串)但它们是有效输入时特别有用。
示例讲解
基础示例
// 使用 ?? 运算符
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" (可能不是期望的结果)
深入理解
让我们看看不同值的表现:
// 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
链式使用
??
运算符可以链式使用,返回第一个不是 null
或 undefined
的值:
const firstChoice = null;
const secondChoice = undefined;
const thirdChoice = 'Available';
const fallback = 'Default';
const result = firstChoice ?? secondChoice ?? thirdChoice ?? fallback;
console.log(result); // 输出: "Available"
使用场景
1. 用户配置与默认值
当处理用户配置或首选项时,??
运算符非常有用:
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
:
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. 表单输入处理
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" }
注意事项
运算符优先级
??
运算符具有相对较低的优先级,因此在复杂表达式中可能需要使用括号:
// 没有括号,会先计算 null || 'foo'
const result1 = null || 'foo' ?? 'bar'; // SyntaxError
// 正确使用括号明确优先级
const result2 = (null || 'foo') ?? 'bar'; // "foo"
出于安全考虑,将 ??
直接与 AND(&&
)和 OR(||
)运算符一起使用是不允许的,会导致语法错误。必须使用括号明确表达你的意图。
短路求值
与 &&
和 ||
类似,??
也具有短路求值的特性。如果左侧表达式不为 null
或 undefined
,右侧表达式将不会被求值:
function getDefaultValue() {
console.log('Generating default value');
return 'default';
}
const value = 'actual' ?? getDefaultValue();
// "Generating default value" 不会被输出,因为左侧值不是 null 或 undefined
实际应用案例
用户个人资料展示
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));
// 使用邮箱作为显示名,保留空字符串作为简介,使用国家作为位置
处理环境变量
// 在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}`);
浏览器兼容性
空值合并运算符是比较新的特性,在一些旧浏览器中可能不支持。如果需要兼容旧浏览器,可以使用如下方式模拟:
// ES2020 空值合并运算符
const result = value ?? defaultValue;
// 旧浏览器兼容方式
const result = (value !== null && value !== undefined) ? value : defaultValue;
或使用 Babel 等转译工具处理。
总结
空值合并运算符 ??
提供了一种简洁、明确的方式来处理 null
和 undefined
值,同时保留其他假值。与逻辑或运算符 ||
相比,它更加精确,尤其适用于:
- 在不丢失有效假值(如
0
或''
)的情况下设置默认值 - 处理可能缺失的数据或配置选项
- 构建条件回退链,选择第一个存在的值
通过合理使用空值合并运算符,你可以使代码更简洁、更可预测,并且避免许多与默认值相关的常见陷阱。
练习
- 编写一个函数,接受用户对象,并返回格式化的用户信息,使用
??
处理缺失的字段。 - 创建一个表单验证函数,使用
??
运算符为空输入提供默认值。 - 比较
??
和||
在处理以下值时的不同表现:0
,''
,false
,null
,undefined
。
额外资源
通过掌握空值合并运算符,你将能更精确地控制默认值的行为,提高代码质量和可维护性。