JS `Nullish Coalescing Operator (??)`:精确处理 `null` 和 `undefined` 默认值

各位代码世界的探险家们,早上好!今天咱们聊聊JavaScript里一个不起眼,但关键时刻能救命的小英雄——空值合并运算符(Nullish Coalescing Operator,简称??)。

啥是空值合并运算符?(别怕,名字唬人!)

简单来说,?? 就像一个精明的守门员,专门负责拦截 nullundefined 这两个捣蛋鬼。它的作用是:如果左边的表达式是 nullundefined,那就返回右边的表达式;否则,直接返回左边的表达式。

用人话说:左边是空的(nullundefined),就用右边的值;否则,用左边的值。

举个栗子:

const name = null;
const defaultName = "匿名用户";

const displayName = name ?? defaultName;

console.log(displayName); // 输出: "匿名用户"

在这个例子里,namenull,所以 ?? 立刻让 displayName 变成了 "匿名用户"。

为啥我们需要它?它和 || 有啥区别?

你可能会想:这玩意儿看起来和逻辑或运算符 || 差不多嘛!都是给变量设置默认值,有啥区别?

区别大了!

|| 会把左边的表达式转换成布尔值,只要是 false0"" (空字符串),NaNnullundefined,都会返回右边的表达式。也就是说,|| 认为这些都是“假”的,都要被替换掉。

?? 就不一样了,它只认 nullundefined,其他的都放行。

再举个栗子,让你明白透彻:

const count = 0;

// 使用 ||
const result1 = count || 10;
console.log(result1); // 输出: 10 (因为 0 被认为是 false)

// 使用 ??
const result2 = count ?? 10;
console.log(result2); // 输出: 0 (因为 0 不是 null 或 undefined)

看到没? || 粗暴地把 0 当成了 "假" 值,直接替换成了 10。而 ?? 则坚守原则,认为 0 是一个有效值,所以 result2 仍然是 0

表格对比,一目了然:

运算符 作用 判定“空”的标准
|| 返回第一个真值,否则返回最后一个值 (常用于提供默认值) false, 0, "", NaN, null, undefined
?? 返回第一个不是 nullundefined 的值,否则返回最后一个值 (专门用于处理 nullundefined 的默认值) null, undefined

使用场景:

?? 最擅长处理那些可能为 nullundefined 的变量,并提供一个合理的默认值。

  • 读取对象属性:

    const user = {
      profile: {
        age: 30,
      },
    };
    
    // 使用 ?? 安全地获取 age,如果 profile 或 age 不存在,则返回 0
    const age = user?.profile?.age ?? 0;
    console.log(age); // 输出: 30
    
    const user2 = { };
    
    const age2 = user2?.profile?.age ?? 0;
    console.log(age2); // 输出: 0

    这里用到了可选链操作符 ?.,它可以安全地访问深层嵌套的属性,如果中间有任何一个属性不存在,就会返回 undefined。然后,?? 再接手,如果 ?. 返回的是 nullundefined,就返回默认值 0

  • 处理函数参数:

    function greet(name) {
      const displayName = name ?? "Guest";
      console.log(`Hello, ${displayName}!`);
    }
    
    greet("Alice"); // 输出: Hello, Alice!
    greet(null);   // 输出: Hello, Guest!
    greet();      // 输出: Hello, Guest! (因为没传参数,name 是 undefined)

    如果调用 greet 函数时没有传递 name 参数,或者传递的是 nulldisplayName 就会使用默认值 "Guest"。

  • 配置项:

    const config = {
      timeout: null, // 用户故意设置为null
      maxRetries: undefined, // 用户没配置
    };
    
    const finalTimeout = config.timeout ?? 5000;
    const finalMaxRetries = config.maxRetries ?? 3;
    
    console.log(finalTimeout); // 输出: null (用户设置的值应该保留)
    console.log(finalMaxRetries); // 输出: 3 (使用默认值)

    这里,我们希望允许用户显式地将 timeout 设置为 null,表示禁用超时。如果使用 ||,就会错误地将 timeout 替换为 5000。而 ?? 则可以正确地处理这种情况。

优先级问题:

?? 的优先级比较低,所以在使用时要特别注意,必要时要加上括号。

举个反面教材:

// 错误示例:
// const result = false || null ?? "default"; // 报错:SyntaxError: Unexpected token '??'

// 正确示例:
const result1 = false || (null ?? "default");
console.log(result1); // 输出: false

const result2 = (false || null) ?? "default";
console.log(result2); // 输出: "default"

因为 ?? 的优先级低于 ||&&,所以不能直接混合使用,必须用括号明确优先级。

和可选链操作符 ?. 配合使用,威力无穷!

前面已经提到,?? 经常和 ?. 一起使用,?. 负责安全地访问对象属性,?? 负责提供默认值。

const user = {};

const city = user?.address?.city ?? "Unknown";
console.log(city); // 输出: "Unknown"

即使 user 对象没有 address 属性,或者 address 对象没有 city 属性,这段代码也不会报错,而是会安全地返回 "Unknown"。

不支持的浏览器:

需要注意的是,?? 是 ES2020 引入的特性,一些老旧的浏览器可能不支持。因此,在使用时要做好兼容性处理。可以使用 Babel 等工具进行代码转换,使其能在旧版本浏览器上运行。

一个更复杂的例子:

假设我们有一个用户对象,包含各种信息,有些信息可能缺失:

const user1 = {
  name: "小明",
  age: 18,
  address: {
    city: "北京",
  },
  preferences: {
    theme: "dark",
  },
};

const user2 = {
  name: "小红",
  age: null, // 年龄未知
  address: null, // 地址未知
  preferences: {}, // 偏好设置存在,但为空
};

const user3 = {
  name: "小刚",
  preferences: undefined, // 偏好设置不存在
};

现在,我们要编写一个函数,根据用户对象生成一段描述:

function generateDescription(user) {
  const name = user.name ?? "匿名用户";
  const age = user.age ?? "年龄未知";
  const city = user?.address?.city ?? "未知城市";
  const theme = user?.preferences?.theme ?? "light"; // 默认主题是 light

  return `
    用户姓名:${name}
    年龄:${age}
    城市:${city}
    主题:${theme}
  `;
}

console.log(generateDescription(user1));
/*
    用户姓名:小明
    年龄:18
    城市:北京
    主题:dark
*/

console.log(generateDescription(user2));
/*
    用户姓名:小红
    年龄:年龄未知
    城市:未知城市
    主题:light
*/

console.log(generateDescription(user3));
/*
    用户姓名:小刚
    年龄:年龄未知
    城市:未知城市
    主题:light
*/

在这个例子中,我们使用了 ???. 来处理各种可能为空的情况,保证了代码的健壮性。

总结:

?? 空值合并运算符是一个小而美的工具,它可以帮助我们更优雅地处理 nullundefined,避免一些潜在的错误。记住,它只认 nullundefined,其他的都放行。在合适的场景下使用 ??,可以使你的代码更加简洁、安全、易读。

希望今天的讲解对你有所帮助!记住,代码世界充满了乐趣,保持好奇心,不断探索,你就能成为一名真正的代码探险家!下次有机会再和大家分享其他的编程技巧。祝大家编码愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注