JS `Nullish Coalescing Operator (??)` (ES2020):空值合并运算符与逻辑短路

各位观众,大家好!今天咱们来聊聊 JavaScript 里一个挺有意思的小家伙——空值合并运算符(Nullish Coalescing Operator),简称 ??。这玩意儿从 ES2020 开始加入战场,专门用来解决一些关于“空”的问题。别看它长得像个问号,威力可不小,能让你的代码更简洁、更安全。

一、啥是空值合并运算符?

先来个简单的定义:空值合并运算符 ?? 是一种逻辑运算符。当左侧的操作数为 nullundefined 时,它会返回右侧的操作数;否则,返回左侧的操作数。

听起来有点绕?没关系,咱们上代码:

let name = null;
let defaultName = "Anonymous";

let displayName = name ?? defaultName;

console.log(displayName); // 输出: "Anonymous"

在这个例子里,namenull,所以 name ?? defaultName 就返回了 defaultName,也就是 "Anonymous"。

如果 name 有值呢?

let name = "Alice";
let defaultName = "Anonymous";

let displayName = name ?? defaultName;

console.log(displayName); // 输出: "Alice"

这回 name 是 "Alice",所以 name ?? defaultName 就直接返回了 "Alice"。

二、??|| 的区别:一个“空”的陷阱

可能有人会说:“这玩意儿跟逻辑或 || 好像啊!” 没错,它们乍一看很像,但区别大了去了。|| 是个“真值判断器”,只要左侧的操作数能被转换成 true,它就返回左侧的值。这意味着,false0""(空字符串)等等,都会被 || 当成“假”来处理。

let quantity = 0;
let defaultQuantity = 10;

let actualQuantity = quantity || defaultQuantity;

console.log(actualQuantity); // 输出: 10

在这个例子里,quantity0|| 认为 0 是“假”的,所以返回了 defaultQuantity,也就是 10。但实际上,我们可能希望 quantity0 时,actualQuantity 就是 0,而不是 10

这时候,?? 就派上用场了:

let quantity = 0;
let defaultQuantity = 10;

let actualQuantity = quantity ?? defaultQuantity;

console.log(actualQuantity); // 输出: 0

?? 只关心左侧是不是 nullundefined0 并不是 nullundefined,所以它会直接返回 quantity,也就是 0

用一张表格来总结一下:

运算符 左侧操作数 返回值
|| null 右侧操作数
|| undefined 右侧操作数
|| false 右侧操作数
|| 0 右侧操作数
|| "" 右侧操作数
?? null 右侧操作数
?? undefined 右侧操作数
?? false 左侧操作数
?? 0 左侧操作数
?? "" 左侧操作数

三、?? 的优先级:小心“左右为难”

在复杂的表达式里,运算符的优先级是个很重要的概念。?? 的优先级相对较低,比 ||&& 都要低。这意味着,如果你想同时使用 ??||&&,你需要用括号来明确优先级。

// 错误写法,会报错!
// let result = a || b ?? c;

// 正确写法:
let result = a || (b ?? c);
let result2 = (a ?? b) || c;

为什么会报错呢?因为 JavaScript 引擎不知道你是想先算 a || b 再和 c?? 运算,还是想先算 b ?? c 再和 a|| 运算。所以,为了避免歧义,必须用括号来明确优先级。

四、?? 的实际应用场景:让代码更优雅

  1. 处理 API 返回值:

    很多时候,我们从 API 获取数据,但 API 可能会返回 nullundefined。使用 ?? 可以很方便地提供默认值:

    let userData = await fetchUserData(); // 假设这个函数从 API 获取用户数据
    
    let userName = userData?.name ?? "Guest"; // 使用可选链式调用 ?. 确保 userData 存在
    let userAge = userData?.age ?? 18;
    
    console.log(`Welcome, ${userName}! You are ${userAge} years old.`);
  2. 配置项默认值:

    在配置对象中,有些配置项可能不是必须的。使用 ?? 可以为这些配置项提供默认值:

    function createButton(options) {
      let text = options.text ?? "Click Me";
      let color = options.color ?? "blue";
      let size = options.size ?? "medium";
    
      console.log(`Creating a ${size} ${color} button with text "${text}"`);
    }
    
    createButton({ color: "red" }); // 输出: Creating a medium red button with text "Click Me"
    createButton({ text: "Submit", size: "large" }); // 输出: Creating a large blue button with text "Submit"
    createButton({}); // 输出: Creating a medium blue button with text "Click Me"
  3. 函数参数默认值:

    虽然 ES6 引入了函数参数默认值,但 ?? 在某些情况下更方便:

    function greet(name) {
      name = name ?? "World";
      console.log(`Hello, ${name}!`);
    }
    
    greet("Alice"); // 输出: Hello, Alice!
    greet(null); // 输出: Hello, World!
    greet(undefined); // 输出: Hello, World!
    greet(""); // 输出: Hello, ! (注意,这里和使用 `||` 的区别)
  4. 链式调用中的安全访问:

    结合可选链式调用 ?.?? 可以安全地访问深层嵌套的对象属性,并提供默认值:

    let user = {
      profile: {
        address: {
          city: "New York",
        },
      },
    };
    
    let city = user?.profile?.address?.city ?? "Unknown";
    
    console.log(city); // 输出: New York
    
    let user2 = {}; // 空对象
    
    let city2 = user2?.profile?.address?.city ?? "Unknown";
    
    console.log(city2); // 输出: Unknown

五、??=:空值赋值运算符

ES2021 又给 ?? 加了个好兄弟——空值赋值运算符 ??=。这个运算符可以将一个变量赋值为右侧的值,当且仅当该变量当前的值为 nullundefined 时。

let username; // 声明但未赋值,所以是 undefined

username ??= "Guest";

console.log(username); // 输出: "Guest"

let age = 25;

age ??= 30;

console.log(age); // 输出: 25 (因为 age 已经有值了)

??= 相当于:

let username;

username = username ?? "Guest"; // 等价于 username ??= "Guest";

??= 可以简化代码,避免重复书写变量名。

六、注意事项

  • ?? 只能用于 nullundefined 的判断,不能用于其他“假值”的判断。
  • 在使用 ??||&& 时,务必使用括号来明确优先级,避免歧义。
  • ??= 只能用于变量的赋值,不能用于其他表达式。

七、总结

空值合并运算符 ?? 是 JavaScript 里一个很有用的工具,可以帮助你更简洁、更安全地处理 nullundefined 的情况。它和逻辑或 || 的区别在于,?? 只关心 nullundefined,而 || 关心所有“假值”。??= 则简化了空值赋值的操作。

掌握了 ??,你的 JavaScript 代码会更加健壮,逻辑会更加清晰。希望今天的讲座能让你对 ?? 有更深入的了解! 咱们下期再见!

发表回复

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