Optional chaining
这是一项新的提案,老旧浏览器可能需要 polyfills。
Optional chaining ?.
(可选链,以下简称 OC)是一种以安全的方式去访问嵌套的对象属性,即使某个属性根本就不存在。
起因:
一个在 JavaScript 中很常见的问题就是:假如用户信息中,地址是可填可不填的,那我们就无法安全地访问地址的某一个属性:
let user = {}; // 用户可能没有填地址
alert(user.address.street); // 报错
或者下面这种情况,在 WEB 开发中,我们可能需要去获取一个 DOM 元素,但是这个 DOM 元素可能不存在:
// 当 querySelector(...) 的结果为 null 的时候,程序会报错
let html = document.querySelector('.my-element').innerHTML;
在 OC ?.
出现之前,我们一般通过逻辑与操作来解决:
let user = {}; // 没有地址的用户
alert( user && user.address && user.address.street ); // undefined (不会报错)
使用逻辑与操作符可以确保表达式的所有部分都能够正确执行,但是写法却比较笨重。
Optional chaining(可选链)
OC ?.
能够使代码变得简便,当位于其前的值为 undefined
或者是 null
时,会立即阻止代码的执行,并且返回 undefined
。
通过 OC,我们可以安全地访问用户的地址:
let user = {}; // 一个没有地址的用户
alert( user?.address?.street ); // undefined (不会报错)
即使 user
对象不存在,使用 OC 访问它的地址属性也不会报错:
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
需要注意的是,OC 仅允许它前面相邻的部分为可选项,不包括进一步的值。
在上面的代码中,user?.
仅允许 user
这个对象为 null
或 undefined
。
另一方面,如果 user
这个对象真的存在,那么 user.address
这个属性必须存在,否则访问 user?.address.street
则会在第二个点这报错。
不要过度使用 OC
我们应该仅在希望某个值可能不存在的情况下才使用?.
例如,根据我们的代码逻辑,user
对象必须存在,但是address
属性是可选的,所以user.address?.street
才是更好的选择。
所以,由于其他原因导致的user
对象为undefined
的情况才能被快速发现。否则,bug 将会变得比较难找
位于
?.
前的变量必须被显示声明
如果user
这个变量根本没有被声明,那么user?.anything
将会触发一个错误:// ReferenceError: user is not defined user?.address;
此处必须有变量声明语句
let/const/var
, OC 对未声明的变量无效
短路
在上文提到,在OC ?.
的前部分的值为 null
或 undefined
的时候,会立即停止执行。
所以,如果在其后面如果有函数的调用,或者其他操作,都不会执行。
let user = null;
let x = 0;
user?.sayHi(x++); // 什么都不会做
alert(x); // 0,值没有自增
其他用法:?.(),?.[]
OC 不是一个操作符,而是一个特别的语法糖,所以可以和函数调用和中括号共用。
例如,?.()
可以用于执行一个可能不存在的函数。
下面的代码中,一些用户拥有 admin
方法,一些用户没有:
let user1 = {
admin() {
alert("I am admin");
}
};
let user2 = {};
user1.admin?.(); // I am admin
user2.admin?.();
我们首先用点语法去获取 admin
方法,因为 user 对象肯定存在,所以我们可以安全地去访问;
然后使用 ?.()
检查其左侧,如果 admin 这个方法存在,就执行(例如 user1),否则就停止执行,但是不报错(例如 user2)。
?.
语法同样可以在当我们需要通过中括号去访问属性时使用,使用它可以安全的访问一个或许还不存在的对象的属性:
let user1 = {
firstName: "John"
};
let user2 = null; // 假如现在不能授权这个用户
let key = "firstName";
alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
alert( user1?.[key]?.something?.not?.existing ) // undefined
?.
同样可以与 delete
操作符共用
delete user?.name; // 删除用户名,如果用户存在的话
使用
?.
可以进行删除和读取操作,但是不能进行赋值操作
在赋值运算符的左侧,?.
无效// 下面的代码是假设现在需要在用户存在的情况下,重新赋值 name 属性 user?.name = "John"; // 报错 // 等同于 undefined = "John"
总结
OC ?.
有三种形式:
obj?.prop
- 如果obj
存在的话,返回obj.prop
的值,否则返回undefined
;obj?.[prop]
- 如果obj
存在的话返回obj[prop]
的值,否则返回undefined
;obj?.method()
- 如果obj
存在的话则调用obj.method()
方法,否则返回undefined
。
这几种形式都非常的直观并且易于使用。?.
检查左侧的值是否为 null
或 undefine
,如果不是的话则继续执行。
通过链式的 ?.
可以安全地访问嵌套的对象。
我们应该仅在当左侧的值可能不存在的情况下才使用 ?.
,这样在发生错误的时候才能更加容易找到问题。
参考链接:
https://javascript.info/optio...
https://developer.mozilla.org...
https://tc39.es/ecma262/#prod...
(完)