中文

掌握 JavaScript 的空值合并运算符 (??),实现更简洁、更高效的默认值赋值。了解它与或运算符 (||) 的区别,并查看实际示例。

JavaScript 空值合并运算符:默认值赋值综合指南

在 JavaScript 中,分配默认值是一项常见任务。传统上,开发人员使用或运算符 (||) 来实现此目的。然而,在 ECMAScript 2020 中引入的空值合并运算符 (??) 提供了一种更精确、更可靠的方式来处理默认值赋值,尤其是在处理 nullundefined 值时。本指南深入探讨了空值合并运算符,探索其语法、行为、与或运算符的区别以及实际用例。

什么是空值合并?

空值合并运算符 (??) 是一个逻辑运算符,当其左侧操作数为 nullundefined 时,它会返回其右侧操作数。否则,它会返回其左侧操作数。简而言之,它仅在变量严格为 nullundefined 时提供默认值。

语法

空值合并运算符的语法非常简单:

leftOperand ?? rightOperand

在这里,leftOperand 是您要检查是否为 nullundefined 的变量或表达式,而 rightOperand 是在 leftOperand 确实为 nullundefined 时您想要分配的默认值。

示例

请看以下示例:

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) 之间的区别至关重要。空值是指 nullundefined假值是在布尔上下文中被视为 false 的值。假值包括:

关键区别在于,空值合并运算符仅检查 nullundefined,而或运算符 (||) 会检查任何假值。

??|| 之间的区别

或运算符 (||) 是一个逻辑或运算符,如果左侧操作数为假值,则返回右侧操作数。虽然它可以用于分配默认值,但在处理像 0 或空字符串这样的值时,可能会导致意外行为。

示例:|| 的陷阱

const quantity = 0 || 10; // 我们希望在 quantity 缺失时默认值为 10
console.log(quantity); // 输出: 10 (意外!) 因为 0 是假值

const text = '' || 'Default Text'; //我们希望在 text 缺失时使用默认文本
console.log(text); // 输出: Default Text (意外!) 因为 '' 是假值

在第一个示例中,我们本意是仅当 quantity 缺失 (nullundefined) 时才分配默认数量 10。然而,由于 0 是一个假值,或运算符错误地分配了默认值。同样,即使字符串存在(但为空),空字符串也会导致显示默认文本。

使用 ?? 以确保精确性

让我们使用空值合并运算符重写前面的示例:

const quantity = 0 ?? 10;
console.log(quantity); // 输出: 0 (正确!)

const text = '' ?? 'Default Text';
console.log(text); // 输出: '' (正确!)

现在,输出符合预期。空值合并运算符仅检查 nullundefined,因此 0'' 被视为有效值,并保留了它们的原始值。

空值合并的用例

空值合并运算符在多种场景下都很有用,当您需要仅在变量严格为 nullundefined 时提供默认值。以下是一些常见的用例:

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. 访问嵌套对象属性

在访问嵌套对象的属性时,空值合并运算符可以与可选链 (?.) 结合使用,以便在任何中间属性为 nullundefined 时提供默认值。

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 或外部源获取数据时,如果某些数据字段缺失或值为 nullundefined,可以使用空值合并运算符提供默认值。考虑从不同地区检索用户数据。假设某些地区的用户数据中可能不包含 `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 引擎无法确定预期的运算顺序。通过添加括号,我们明确告诉引擎首先评估空值合并运算符。第二个示例是有效的;然而,输出不同,因为 `||` 表达式被首先评估。

浏览器兼容性

空值合并运算符 (??) 是一个相对较新的功能,因此考虑浏览器兼容性至关重要,特别是当您的目标是旧版浏览器时。大多数现代浏览器都支持空值合并运算符,包括:

如果您需要支持旧版浏览器,可以使用像 Babel 这样的转译器将您的代码转换为兼容版本的 JavaScript。Babel 会将 ?? 运算符转换为在旧环境中可用的等效 JavaScript 代码。

最佳实践

以下是有效使用空值合并运算符的一些最佳实践:

全局考量

在为全球受众开发时,请考虑以下与默认值分配相关的要点:

示例:使用空值合并进行本地化

假设您想根据用户的区域设置以不同语言显示默认的欢迎消息。如果本地化消息不可用,您可以使用空值合并运算符提供默认消息。

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 代码的质量和可靠性,使其更健壮、更易于理解。请记住,始终在代码中优先考虑清晰性和可维护性,而空值合并运算符可以成为实现这些目标的强大工具。

JavaScript 空值合并运算符:默认值赋值综合指南 | MLOG