各位观众,大家好!今天咱们来聊聊 JavaScript 里一个挺有意思的小家伙——空值合并运算符(Nullish Coalescing Operator),简称 ??
。这玩意儿从 ES2020 开始加入战场,专门用来解决一些关于“空”的问题。别看它长得像个问号,威力可不小,能让你的代码更简洁、更安全。
一、啥是空值合并运算符?
先来个简单的定义:空值合并运算符 ??
是一种逻辑运算符。当左侧的操作数为 null
或 undefined
时,它会返回右侧的操作数;否则,返回左侧的操作数。
听起来有点绕?没关系,咱们上代码:
let name = null;
let defaultName = "Anonymous";
let displayName = name ?? defaultName;
console.log(displayName); // 输出: "Anonymous"
在这个例子里,name
是 null
,所以 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
,它就返回左侧的值。这意味着,false
、0
、""
(空字符串)等等,都会被 ||
当成“假”来处理。
let quantity = 0;
let defaultQuantity = 10;
let actualQuantity = quantity || defaultQuantity;
console.log(actualQuantity); // 输出: 10
在这个例子里,quantity
是 0
,||
认为 0
是“假”的,所以返回了 defaultQuantity
,也就是 10
。但实际上,我们可能希望 quantity
为 0
时,actualQuantity
就是 0
,而不是 10
。
这时候,??
就派上用场了:
let quantity = 0;
let defaultQuantity = 10;
let actualQuantity = quantity ?? defaultQuantity;
console.log(actualQuantity); // 输出: 0
??
只关心左侧是不是 null
或 undefined
,0
并不是 null
或 undefined
,所以它会直接返回 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
做 ||
运算。所以,为了避免歧义,必须用括号来明确优先级。
四、??
的实际应用场景:让代码更优雅
-
处理 API 返回值:
很多时候,我们从 API 获取数据,但 API 可能会返回
null
或undefined
。使用??
可以很方便地提供默认值: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.`);
-
配置项默认值:
在配置对象中,有些配置项可能不是必须的。使用
??
可以为这些配置项提供默认值: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"
-
函数参数默认值:
虽然 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, ! (注意,这里和使用 `||` 的区别)
-
链式调用中的安全访问:
结合可选链式调用
?.
,??
可以安全地访问深层嵌套的对象属性,并提供默认值: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 又给 ??
加了个好兄弟——空值赋值运算符 ??=
。这个运算符可以将一个变量赋值为右侧的值,当且仅当该变量当前的值为 null
或 undefined
时。
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";
??=
可以简化代码,避免重复书写变量名。
六、注意事项
??
只能用于null
和undefined
的判断,不能用于其他“假值”的判断。- 在使用
??
和||
或&&
时,务必使用括号来明确优先级,避免歧义。 ??=
只能用于变量的赋值,不能用于其他表达式。
七、总结
空值合并运算符 ??
是 JavaScript 里一个很有用的工具,可以帮助你更简洁、更安全地处理 null
和 undefined
的情况。它和逻辑或 ||
的区别在于,??
只关心 null
和 undefined
,而 ||
关心所有“假值”。??=
则简化了空值赋值的操作。
掌握了 ??
,你的 JavaScript 代码会更加健壮,逻辑会更加清晰。希望今天的讲座能让你对 ??
有更深入的了解! 咱们下期再见!