在JavaScript中长的链式调用可能容易出错,由于任何一步均可能出现null
或undefined
(也被称为“无效”值)。检查每一个步骤的属性是否存在很容易变成深层次嵌套的if
声明或者复制属性访问链的长的if
条件:javascript
// 容易出错的版本,可能抛出错误
const nameLength = db.user.name.length;
// 不容易出错,可是难以阅读
let nameLength;
if (db && db.user && db.user.name)
nameLength = db.user.name.length;
复制代码
以上还能够使用三元表达式,可是一样难以阅读:java
const nameLength =
(db
? (db.user
? (db.user.name
? db.user.name.length
: undefined)
: undefined)
: undefined);
复制代码
咱们并不想写出这样的代码,因此有一些代替方案是可取的。一些语言(例如swift,具体查看https://www.jianshu.com/p/5599b422afb0)针对这个问题提供了优雅的解决方案——可选调用链。 根据最近的规范,“可选调用链是一个或多个属性访问和函数调用的链,以?.
开头”。git
使用新的可选调用链,咱们能够重写上面的demo:github
// 依然检查错误,可是可读性更高
const nameLength = db?.user?.name?.length;
复制代码
使用可选调用链,当db
,user
,或name
是undefined
或者null
的时候,nameLength
被初始化为undefined
,而不是像以前那样抛出错误。数据库
**Note:**可选调用链比咱们本身用if(db && db.user && db.user.name)
检查更加健壮,例如,若是name
是一个空字符串,可选字符串会将name?.length
改成name.length
而后获得正确的长度0
,可是若是像咱们以前那样作判断,不会获得正确的值,由于在if语句中空字符和false
的行为相同。可选调用链修复了这个常见的bug。swift
还有一个用于调用可选方法的运算符:数组
// 使用可选方法扩展接口,仅适用于管理员用户
const adminOption = db?.user?.validateAdminAndGetPrefs?.().option;
复制代码
这个语法可能有点儿出乎意料,由于这里的运算符是?.()
,该运算符适用于以前的表达式。安全
可选调用链还有第三种用法,便可选动态属性访问?.[]
。若是对象中有该key对应的value,则返回value,不然返回undefined
。demo以下:函数
// 使用动态属性名访问属性对应的值
const optionName = 'optional setting';
const optionLength = db?.user?.preferences?.[optionName].length;
复制代码
该用法一样适用于可选索引数组,例如:ui
// 若是`userArray`是`null`或`undefined`,则`userName`被优雅的赋值为`undefined`
const userIndex = 42;
const userName = usersArray?.[userIndex].name;
复制代码
可选调用链能够和nullish coalescing ?? 操做符结合使用,返回一个非undefined
的默认值。这样能够使用指定的默认值安全的进行深层属性访问,解决了以前用户须要JavaScript库才能解决的问题,例如lodash的_.get:
const object = { id: 123, names: { first: 'Alice', last: 'Smith' }};
{ // With lodash:
const firstName = _.get(object, 'names.first');
// → 'Alice'
const middleName = _.get(object, 'names.middle', '(no middle name)');
// → '(no middle name)'
}
{ // With optional chaining and nullish coalescing:
const firstName = object?.names?.first ?? '(no first name)';
// → 'Alice'
const middleName = object?.names?.middle ?? '(no middle name)';
// → '(no middle name)'
}
复制代码
可选调用链有一些有趣的属性:短路(short-circuiting),堆叠(stacking)和可选删除(optonal deletion)。下面经过例子来了解这些属性。
*短路(short-circuiting)*即若是可选调用链提早返回则不计算表达式的其他部分:
// 只有在`db`和`user`不是`undefined`的状况下`age`才会+1
db?.user?.grow(++age);
复制代码
*堆叠(stacking)*意味着能够在一系列属性访问中应用多个可选调用运算符:
// 一个可选链能够跟随另外一个可选链
const firstNameLength = db.users?.[42]?.names.first.length;
复制代码
可是,考虑在一个链中使用多个可选调用运算符。若是一个value保证是有效的,那么不鼓励使用?.
去访问属性。像在以前的例子中,db
必然是定义的,可是db.users
和db.users[42]
多是未定义的。若是数据库中有这样的用户,那么name.first.length
也是始终被定义的。
*可选删除(optonal deletion)*就是delete
操做符能够和可选链一块儿使用:
// 当且仅当`db`是定义过的时候删除`db.user`
delete db?.user;
复制代码
更多细节能够访问该提案的语义部分。
原文连接:v8.dev/features/op…