今天我们来介绍一个你可能没见过的 JS 新特性,目前处于 Stage 3 阶段,它叫作 可选链(optional chaining),它可能解决很多人都面对过的痛点,让我们来了解下~
想象一下你从某个 api 获取数据,返回的对象嵌套了好多层,这就意味着你需要写很长的属性访问:
// API response object
const person = {
details: {
name: {
firstName: "Michael",
lastName: "Lampe",
}
},
jobs: [
"Senior Full Stack Web Developer",
"Freelancer"
]
}
// Getting the firstName
const personFirstName = person.details.name.firstName;
上面的代码很容易产生错误,我们一般会这么改进:
// Checking if firstName exists
if( person &&
person.details &&
person.details.name ) {
const personFirstName = person.details.name.firstName || 'stranger';
}
可以看到为了访问某个人的 firstName
,代码变得非常不优雅。我们可以用 lodash 来优化一下:
_.get(person, 'details.name.firstName', 'stranger');
lodash 的写法可读性更高,但是需要引入额外的依赖,而且在团队内部大家可能不会统一都这么写,那么有没有更好的办法呢?
可选链
就是为了解决这个问题而诞生的。
可选链在语法上可能看起来比较陌生,但是用了几次之后你就会很容易适应这种写法。
const personFirstName = person?.details?.name?.firstName;
其实就是在属性访问符 .
的前面加了个问号。我们看上面语句中第一个 ?.
,从 JS 层面,它表示如果 person
的值为 null
或者 undefined
,就不会报错而返回 undefined
,否则才继续访问后面的 details
属性。而如果后面的属性访问链中有任何一个属性为 null
或者 undefined
,那么最终的值就为 undefined
。
为了优雅地设置默认值,我们引入另外一个特性:空值合并运算符(nullish-coalescing-operator)
,听起来好像很复杂,其实也很简单:
const personFirstName = person?.details?.name?.firstName ?? 'stranger';
这个运算符就是 ??
,如果它左侧表达式的结果是 undefined
,personFirstName
,就会取右侧的 stranger
。
是不是跟短路运算符 ||
很像,那假如我们把 ??
换成 ||
呢?
上面的例子中,如果 firstName
的值为 0 或者空字符串等非 undefined
的 falsy
值,那么最终的结果就不一样了。
??
就是为了取代 ||
,来做设置默认值这件事的。
如果你需要使用动态属性,同样很简单:
const jobNumber = 1;
const secondJob = person?.jobs?.[jobNumber] ?? 'none';
上面的代码中, jobs?.[jobNumber]
和 jobs[jobNumber]
的含义是一样的,区别就是前者不会报错。
同样的,如果想安全调用一个方法,只需要使用 ?.()
:
const currentJob = person?.jobs.getCurrentJob?.() ?? 'none';
如果 getCurrentJob
不是一个函数,currentJob
的值就是 none
很显然,这个特性的兼容性感人,不过没关系,我们有 babel
!
立刻,马上就能让你使用它:
babel-plugin-proposal-optional-chaining
这个特性在很多其他的语言如 C#
,Swift
中都有实现,并且 TypeScript
中也已经加入该特性。感兴趣的小伙伴还不快尝试一下,如果嫌安装 babel plugin 太麻烦,直接使用 lodash 的 get 也不失为一种保守的选择~
JS new feature: Optional Chaining proposal-optional-chaining babel-plugin-proposal-optional-chaining babel nullish-coalescing-operator