掌握 JavaScript 的空值合并运算符 (??),实现更简洁、更高效的默认值赋值。了解它与或运算符 (||) 的区别,并查看实际示例。
JavaScript 空值合并运算符:默认值赋值综合指南
在 JavaScript 中,分配默认值是一项常见任务。传统上,开发人员使用或运算符 (||
) 来实现此目的。然而,在 ECMAScript 2020 中引入的空值合并运算符 (??
) 提供了一种更精确、更可靠的方式来处理默认值赋值,尤其是在处理 null
或 undefined
值时。本指南深入探讨了空值合并运算符,探索其语法、行为、与或运算符的区别以及实际用例。
什么是空值合并?
空值合并运算符 (??
) 是一个逻辑运算符,当其左侧操作数为 null
或 undefined
时,它会返回其右侧操作数。否则,它会返回其左侧操作数。简而言之,它仅在变量严格为 null
或 undefined
时提供默认值。
语法
空值合并运算符的语法非常简单:
leftOperand ?? rightOperand
在这里,leftOperand
是您要检查是否为 null
或 undefined
的变量或表达式,而 rightOperand
是在 leftOperand
确实为 null
或 undefined
时您想要分配的默认值。
示例
请看以下示例:
const username = null ?? "Guest";
console.log(username); // 输出: Guest
const age = undefined ?? 25;
console.log(age); // 输出: 25
const city = "London" ?? "Unknown";
console.log(city); // 输出: London
在此示例中,username
被赋予了默认值 "Guest",因为它最初是 null
。同样,age
被赋予了 25,因为它最初是 undefined
。然而,city
保留了其原始值 "London",因为它既不是 null
也不是 undefined
。
空值 vs. 假值
在 JavaScript 中,理解空值 (nullish) 和假值 (falsy) 之间的区别至关重要。空值是指 null
或 undefined
。假值是在布尔上下文中被视为 false 的值。假值包括:
null
undefined
0
(零)NaN
(Not a Number)''
(空字符串)false
关键区别在于,空值合并运算符仅检查 null
或 undefined
,而或运算符 (||
) 会检查任何假值。
??
与 ||
之间的区别
或运算符 (||
) 是一个逻辑或运算符,如果左侧操作数为假值,则返回右侧操作数。虽然它可以用于分配默认值,但在处理像 0
或空字符串这样的值时,可能会导致意外行为。
示例:||
的陷阱
const quantity = 0 || 10; // 我们希望在 quantity 缺失时默认值为 10
console.log(quantity); // 输出: 10 (意外!) 因为 0 是假值
const text = '' || 'Default Text'; //我们希望在 text 缺失时使用默认文本
console.log(text); // 输出: Default Text (意外!) 因为 '' 是假值
在第一个示例中,我们本意是仅当 quantity
缺失 (null
或 undefined
) 时才分配默认数量 10。然而,由于 0
是一个假值,或运算符错误地分配了默认值。同样,即使字符串存在(但为空),空字符串也会导致显示默认文本。
使用 ??
以确保精确性
让我们使用空值合并运算符重写前面的示例:
const quantity = 0 ?? 10;
console.log(quantity); // 输出: 0 (正确!)
const text = '' ?? 'Default Text';
console.log(text); // 输出: '' (正确!)
现在,输出符合预期。空值合并运算符仅检查 null
或 undefined
,因此 0
和 ''
被视为有效值,并保留了它们的原始值。
空值合并的用例
空值合并运算符在多种场景下都很有用,当您需要仅在变量严格为 null
或 undefined
时提供默认值。以下是一些常见的用例:
1. 处理可选的函数参数
在定义带有可选参数的函数时,您可以使用空值合并运算符在未提供参数时提供默认值。
function greet(name, greeting) {
const userName = name ?? "User";
const userGreeting = greeting ?? "Hello";
console.log(`${userGreeting}, ${userName}!`);
}
greet(); // 输出: Hello, User!
greet("Alice"); // 输出: Hello, Alice!
greet("Bob", "Greetings"); // 输出: Greetings, Bob!
2. 设置默认配置选项
在使用配置对象时,您可以使用空值合并运算符来确保在未指定某些配置选项时使用默认值。
const config = {
timeout: 5000,
retries: 3
};
function fetchData(options) {
const timeout = options.timeout ?? 10000; // 默认超时时间 10 秒
const retries = options.retries ?? 5; // 默认 5 次重试
console.log(`Timeout: ${timeout}, Retries: ${retries}`);
}
fetchData(config); // 输出: Timeout: 5000, Retries: 3
fetchData({}); // 输出: Timeout: 10000, Retries: 5
fetchData({timeout:null, retries: undefined}); // 输出: Timeout: 10000, Retries: 5
3. 访问嵌套对象属性
在访问嵌套对象的属性时,空值合并运算符可以与可选链 (?.
) 结合使用,以便在任何中间属性为 null
或 undefined
时提供默认值。
const user = {
profile: {
address: {
city: "New York"
}
}
};
const cityName = user?.profile?.address?.city ?? "Unknown";
console.log(cityName); // 输出: New York
const unknownUser = {};
const unknownCityName = unknownUser?.profile?.address?.city ?? "Unknown";
console.log(unknownCityName); // 输出: Unknown
4. 处理 API 和外部数据
从 API 或外部源获取数据时,如果某些数据字段缺失或值为 null
或 undefined
,可以使用空值合并运算符提供默认值。考虑从不同地区检索用户数据。假设某些地区的用户数据中可能不包含 `country` 字段。
async function getUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
const data = await response.json();
const country = data.country ?? "Unknown Country";
const timezone = data.timezone ?? "UTC";
console.log(`User is from: ${country}, Timezone: ${timezone}`);
} catch (error) {
console.error("获取用户数据时出错:", error);
}
}
// 模拟不同的 API 响应:
const userWithCountry = { name: "John", country: "USA", timezone: "EST" };
const userWithoutCountry = { name: "Jane", timezone: "GMT" };
// 要测试此代码,您需要一个真实的 API 或模拟 fetch。
// 为演示目的,我们来模拟响应:
global.fetch = async (url) => {
if (url.includes("123")) {
return { json: async () => userWithCountry };
} else if (url.includes("456")) {
return { json: async () => userWithoutCountry };
}
throw new Error("Unexpected URL");
};
getUserData(123); // 输出: User is from: USA, Timezone: EST
getUserData(456); // 输出: User is from: Unknown Country, Timezone: GMT
运算符优先级
空值合并运算符的优先级相对较低。它低于或 (||
) 和与 (&&
) 运算符。因此,当将空值合并运算符与其他逻辑运算符结合使用时,必须使用括号来明确定义运算顺序。否则可能导致语法错误或意外行为。
示例:使用括号以保证清晰
// 不使用括号 (语法错误)
// const result = false || null ?? "Default"; // SyntaxError: Unexpected token '??'
// 使用括号 (正确)
const result = false || (null ?? "Default");
console.log(result); // 输出: Default
const anotherResult = (false || null) ?? "Default";
console.log(anotherResult); // 输出: null
在第一个示例中,缺少括号导致了 SyntaxError
,因为 JavaScript 引擎无法确定预期的运算顺序。通过添加括号,我们明确告诉引擎首先评估空值合并运算符。第二个示例是有效的;然而,输出不同,因为 `||` 表达式被首先评估。
浏览器兼容性
空值合并运算符 (??
) 是一个相对较新的功能,因此考虑浏览器兼容性至关重要,特别是当您的目标是旧版浏览器时。大多数现代浏览器都支持空值合并运算符,包括:
- Chrome 80+
- Firefox 72+
- Safari 13.1+
- Edge 80+
- Node.js 14+
如果您需要支持旧版浏览器,可以使用像 Babel 这样的转译器将您的代码转换为兼容版本的 JavaScript。Babel 会将 ??
运算符转换为在旧环境中可用的等效 JavaScript 代码。
最佳实践
以下是有效使用空值合并运算符的一些最佳实践:
- 使用
??
进行空值检查:当您只想在变量为null
或undefined
时提供默认值时,请使用空值合并运算符 (??
)。 - 对复杂表达式使用括号:当将空值合并运算符与其他逻辑运算符结合使用时,请使用括号来清晰地定义运算顺序。
- 考虑浏览器兼容性:检查浏览器兼容性,并在必要时使用转译器以支持旧版浏览器。
- 保持一致性:在适当的地方采用
??
,以便在整个项目中形成更可预测的编码风格。 - 与可选链结合使用:将
??
与可选链?.
结合使用,以安全地访问嵌套对象属性并为其分配默认值。
全局考量
在为全球受众开发时,请考虑以下与默认值分配相关的要点:
- 本地化:默认值可能需要根据用户的语言或地区进行本地化。例如,默认的货币符号或日期格式。
- 文化规范:某些默认值可能需要根据文化规范进行调整。例如,在不同文化中,默认的问候消息可能需要不同。
- 可访问性:确保默认值对于残障用户是可访问和可理解的。在用户界面中为默认值提供清晰和描述性的标签。
- 时区和日期:处理日期和时间时,请使用适当的时区和日期格式,以确保默认值能正确显示给不同地区的用户。
示例:使用空值合并进行本地化
假设您想根据用户的区域设置以不同语言显示默认的欢迎消息。如果本地化消息不可用,您可以使用空值合并运算符提供默认消息。
function getWelcomeMessage(locale) {
const localizedMessages = {
en: "Welcome!",
fr: "Bienvenue !",
de: "Willkommen!"
};
const message = localizedMessages[locale] ?? "Welcome!"; // 如果未找到区域设置,则默认为英语
return message;
}
console.log(getWelcomeMessage("fr")); // 输出: Bienvenue !
console.log(getWelcomeMessage("es")); // 输出: Welcome! (默认为英语)
结论
空值合并运算符 (??
) 是 JavaScript 语言的一个宝贵补充。与或运算符 (||
) 相比,它提供了一种更精确、更可靠的方式来分配默认值,尤其是在处理像 0
或空字符串这样的值时。通过理解其语法、行为和用例,您可以编写更清晰、更易于维护的代码,从而准确地处理默认值赋值。在您的项目中使用空值合并运算符时,请记住考虑浏览器兼容性、运算符优先级和全局考量。
通过遵循本指南中概述的最佳实践,您可以有效地利用空值合并运算符来提高 JavaScript 代码的质量和可靠性,使其更健壮、更易于理解。请记住,始终在代码中优先考虑清晰性和可维护性,而空值合并运算符可以成为实现这些目标的强大工具。