ES2021 引入了三个逻辑赋值运算符:||=、&&=、??=。这三个符号都是”条件赋值”的简写,但触发条件各不相同,混淆了容易出 bug。
||=(逻辑或赋值)
只有左侧为 falsy 时才赋值:
a ||= b
// 等价于
a = a || b
// 即:if (!a) a = b
let x = null;
x ||= "default";
console.log(x); // "default"
let y = 0;
y ||= 100;
console.log(y); // 100 ← 0 是 falsy,会被覆盖
会触发赋值的值:null、undefined、false、0、''、NaN
典型用途:缓存计算结果
function checkIsRowVisible(data) {
viewportRect ||= scrollContainer.getBoundingClientRect();
// 第一次为 undefined,执行右侧并缓存
// 之后 viewportRect 有值,跳过
const rowEl = data._rowEl.getBoundingClientRect();
return rowEl.bottom >= viewportRect.top && rowEl.top <= viewportRect.bottom;
}
??=(空值合并赋值)
只有左侧为 null 或 undefined 时才赋值:
a ??= b
// 等价于
a = a ?? b
// 即:if (a === null || a === undefined) a = b
let x = null;
x ??= "default";
console.log(x); // "default"
let y = 0;
y ??= 100;
console.log(y); // 0 ← 0 不是 null/undefined,不会被覆盖
??= 比 ||= 更”精准”:0、false、'' 都不会触发赋值。
典型用途:对象属性的惰性初始化
user.preferences ??= {};
user.preferences.theme ??= "dark";
&&=(逻辑与赋值)
只有左侧为 truthy 时才赋值:
a &&= b
// 等价于
a = a && b
// 即:if (a) a = b
let arr = [1, 2, 3];
arr &&= arr.map(x => x * 2);
console.log(arr); // [2, 4, 6]
let empty = null;
empty &&= [1, 2, 3];
console.log(empty); // null ← null 是 falsy,不赋值
典型用途:只在值存在时更新
element &&= element.querySelector(".item");
// element 为 null 时不执行,避免报错
三者对比
| 运算符 | 触发赋值条件 | 不触发的值 |
|---|---|---|
||= | 左侧为 falsy | truthy 值 |
??= | 左侧为 null/undefined | 0, false, ”, NaN 等 |
&&= | 左侧为 truthy | falsy 值 |
let a = 0;
a ||= 1; // a = 1 (0 是 falsy)
a = 0;
a ??= 1; // a = 0 (0 不是 null/undefined)
a = 0;
a &&= 1; // a = 0 (0 是 falsy,不赋值)
let b = 5;
b ||= 1; // b = 5 (5 是 truthy,不赋值)
b ??= 1; // b = 5 (5 不是 null/undefined,不赋值)
b &&= 10; // b = 10 (5 是 truthy,赋值)
选哪个
- 想要”没有值就用默认值”,且
0/false/''也算有效值 → 用??= - 想要”空/假就用默认值” → 用
||= - 想要”有值才更新” → 用
&&=
对于对象初始化类的场景,??= 更安全,不会意外覆盖 false 或 0 这类合法值。
