可选链
可选链解决了什么问题?
- 痛点: 访问深层属性时中间某层可能为
null/undefined, 直接取属性抛TypeError; - 旧写法一:
user.address ? user.address.street : undefined, 属性路径重复书写, 嵌套更深时几乎不可读; - 旧写法二:
user.address && user.address.street, 虽能短路, 但user.address仍出现多次; - 附带问题:
document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null会把查找执行两次;
const user = {}; // 没有 address
user.address.street; // TypeError
user?.address?.street; // undefined, 不报错
- 定位:
?.是"安全读取"的语法糖, 缺东西时返回undefined而不是报错; - 术语约定: 后文说某值"存在"指它不是
null也不是undefined;
?. 有哪几种写法?
| 写法 | 无 ?. 时等价于 | 左值为 null/undefined 时 |
|---|---|---|
obj?.prop | obj.prop | 返回 undefined |
obj?.[expr] | obj[expr] | 返回 undefined |
func?.() | func() | 返回 undefined |
obj.method?.() | obj.method() | 返回 undefined |
- 本质: 不是运算符, 而是特殊语法结构, 因此能覆盖点、方括号与调用三种位置;
const userAdmin = { admin: () => "admin" };
const userGuest = {};
userAdmin.admin?.(); // "admin"
userGuest.admin?.(); // undefined, 方法不存在也不报错
const user2 = null;
user2?.["firstName"]; // undefined
- 混合使用:
user?.address?.["street"]、arr?.[0]?.name等可以逐层衔接;
短路发生在哪一层?
- 只保证
?.左侧: 在user?.address.street.name中仅user可为空,.street与.name仍按普通访问求值; - 逐层可选: 每一层都允许缺失时, 必须逐层写
?., 一个?.不会向后传递; - 短路副作用: 左侧不存在时, 右侧的函数调用与表达式一概不求值;
let x = 0;
const user = null;
user?.sayHi(x++); // 不进入调用, x++ 不执行
console.log(x); // 0
- 声明前提: 变量必须先声明, 否则
user?.anything仍抛ReferenceError,?.只处理值不存在, 不处理标识符不存在;
?. 与 &&、?? 如何配合?
- 与
&&:user.address && user.address.street用真值判断,0、""等假值会被误判为缺失;?.只判断null/undefined; - 与
??:?.缺失时得到undefined, 需要兜底值就接??;
const user = {};
user.address?.street ?? "未知街道"; // "未知街道"
可选链能用于赋值与 delete 吗?
- 安全删除: 可以,
delete user?.name只在user存在时执行删除; - 安全写入: 不可以,
?.不能出现在赋值左侧;
delete user?.name; // user 为空时什么也不做
let user2 = null;
user2?.name = "John"; // TypeError, 等价于 undefined = "John"
什么时候不该用可选链?
- 原则: 只在该值"按业务逻辑允许缺失"时使用;
- 反例:
user按逻辑必然存在而address可选时, 写user.address?.street, 而不是user?.address?.street; - 原因: 过度使用会把编程错误静默成
undefined, 让 bug 更难定位; - 兼容性: 属于较新语法, 老环境需转译 (Babel 等);
- 边界:
?.与??的判空口径一致, 与==/隐式转换无关, 见 隐式转换与判等;