逻辑赋值运算符(&&=、||=、??=)
更简洁地根据条件赋值:
javascript
let a = 0;
let b = null;
let c = "x";
a ||= 42; // a 为假值时赋右值 -> 42(注意 0 也是假值)
b ??= 7; // 仅当 null/undefined 时赋值 -> 7
c &&= c + "!"; // 仅当真值时更新 -> 'x!'
1
2
3
4
5
6
7
2
3
4
5
6
7
与可选链/空值合并结合可减少样板代码。
运算规则回顾
a ||= b
等价于a || (a = b)
:当a
为假值(falsey:0、''、false、NaN、null、undefined)时赋值;a &&= b
等价于a && (a = b)
:当a
为真值(truthy)时赋值;a ??= b
等价于a ?? (a = b)
:仅当a
是 null 或 undefined 时赋值(不把 0/''/false 当空值)。
javascript
let retries = 0;
retries ||= 3; // 若已有次数(>0)则不覆盖;若为 0 也会被覆盖 -> 注意!
let timeout = null;
timeout ??= 5000; // null/undefined 才赋值 -> 更适合作默认
let token = getTokenOrFalse();
token &&= token.trim(); // 有值再规范化
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
与传统写法的对比
javascript
// 传统
if (!a) a = b;
// 现代
a ||= b;
// 传统
if (a) a = transform(a);
// 现代
a &&= transform(a);
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
优势:更短更直观,避免重复 a
。
搭配可选链与空值合并
javascript
// 可选属性若为空则赋默认对象
settings.user ??= {};
// 深层可选链赋值需先确保上层存在
config.options ??= {};
config.options.cache ??= { ttl: 60000 };
// 与可选调用结合
handler?.onInit &&= handler.onInit.bind(handler);
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
常见模式
- 默认参数与状态初始化:
state.count ??= 0
; - 懒加载资源:
conn ??= await connect()
; - 条件规范化:
input &&= input.trim()
; - 仅当未设置时启用功能开关:
flags.enableX ??= true
。
容易踩坑
||=
会把 0/''/false 当成“需要覆盖”,很多场景不符合预期,应改用??=
;- 不要用于只读或 getter 属性(可能抛错或产生副作用);
- 左侧表达式会被求值一次,但注意若是带副作用的表达式会产生意料之外的效果。
最佳实践
- 默认值优先
??=
,除非确实要把 0/''/false 视为“未设定”; - 深层对象赋默认前先用
??=
确保链路存在; - 搭配可选链、空值合并可显著减少样板代码;
- 写单元测试覆盖边界值(0、''、false、null、undefined)。
练习
- 将一段
if (!x) x = default;
风格的初始化改为||=
与??=
两版,并列出差异; - 为一个可选配置对象构建安全的默认层级,使用
??=
与可选链; - 写一个函数,清理用户输入对象:对每个字符串字段执行
&&= .trim()
,对未设定字段用??=
赋默认。