JavaScript 新提案:optional chaining(可选链)

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 这个对象为 nullundefined
另一方面,如果 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 ?. 的前部分的值为 nullundefined 的时候,会立即停止执行。
所以,如果在其后面如果有函数的调用,或者其他操作,都不会执行。

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 ?. 有三种形式:

  1. obj?.prop - 如果 obj 存在的话,返回 obj.prop 的值,否则返回 undefined
  2. obj?.[prop] - 如果 obj 存在的话返回 obj[prop] 的值,否则返回 undefined
  3. obj?.method() - 如果 obj 存在的话则调用 obj.method() 方法,否则返回 undefined

这几种形式都非常的直观并且易于使用。?. 检查左侧的值是否为 nullundefine,如果不是的话则继续执行。

通过链式的 ?. 可以安全地访问嵌套的对象。

我们应该仅在当左侧的值可能不存在的情况下才使用 ?.,这样在发生错误的时候才能更加容易找到问题。

参考链接:
https://javascript.info/optio...
https://developer.mozilla.org...
https://tc39.es/ecma262/#prod...

(完)

你可能感兴趣的:(javascriptes6)