是时候学习/推广一波可选链(Optional chaining)和空值合并(Nullish coalescing )了

最近工做中发现团队有些同窗不太了解 Optional chainingNullish coalescing 两个新的操做符,正好推广一波shell

背景

Optional chainingNullish coalescing 目前都已经归入 ECMA-262 标准中,不过兼容性还差得远,以下: json

MDN 兼容表
两个操做符的兼容几乎一致,不过如今有了 babel,兼容都不是问题。不过仍是要注意使用前必定要确认项目是否支持这俩操做符,切勿只顾一时爽,至于如何兼容能够看下方。

Optional chaining 介绍

Optional chaining 是为了解决程序中铺天盖地的 Cannot read property 'foo' of undefined 错误或者是满屏幕的 a && a.b && a.b.c && a.b.c.d 逻辑与运算符或者是三元操做符。 有了 Optional chaining,咱们能够十分优雅的去获取某些可能不存在的数据。缓存

Optional chaining 操做符的定义是:当左操做数为空值时(nullundefined)中断取值操做并返回 undefined。(方法调用为方法为空值时中断调用并返回 undefined)。babel

const foo = a?.b?.c?.d;
复制代码

比起一长串的逻辑与运算符,不但优雅美观,并且方便、可读性更高,逻辑与有时候会偷懒不写,不过自从用了 Optional chaining,不再用偷懒了,属性取值如此简单稳定,不再怕属性找不到了。ui

Optional chaining 有三种标准语法:spa

// 静态属性
a?.b?.c.d
// 动态属性
a?.[b]?.c.d
// 方法调用
a?.b?.()
复制代码

不过也须要注意 Optional chaining 后不能跟数字,由于存在语法上的重合。插件

a?.3:0;
a?.[3]
复制代码

因此须要接数字时记得使用 []eslint

同时 Optional chaining 也支持 delete:code

delete a?.b?.c;
复制代码

注意上述操做不管 a.b 的值只会删除 a.b.c 不会删除 a.bcdn

Nullish coalescing 介绍

再来看看 Nullish coalescingNullish coalescingOptional chaining 算是一对好基友,主要用来作一些默认值的设置。

Nullish coalescing 操做符的定义是:当左操做数为空值时(nullundefined)返回右操做数,不然返回左操做数。

const foo = a?.b?.c?.d ?? 'bar';
复制代码

有的同窗可能好奇这不是和逻辑或同样吗?

const foo = a?.b?.c?.d || 'bar';
复制代码

其实仍是不同的 Nullish coalescing 从名字能够看出来:空值合并,也就是只有左操做数为空值时才会应用右操做数,而逻辑或使用的是假值进行判断,在一些边界状况下如左操做数为 0'' 空字符串时 Nullish coalescing 会更合理,能够减小一些边界值的判断。

null ?? 'foo'; // 'foo'
undefined ?? 'foo'; // 'foo'
0 ?? 'foo'; // 0
'' ?? 'foo'; // ''

null || 'foo'; // 'foo'
undefined || 'foo'; // 'foo'
0 || 'foo'; // 'foo'
'' || 'foo'; // 'foo'
复制代码

如何使用

这两个新的操做符其实如今已经包含在新版的 preset-env 中,若是你的项目 preset-env 较新的化,那恭喜🎉,你不须要作什么额外的操做就能够用上了。

不过若是是较旧版的 preset-env,那么须要安装上相应的插件来进行启用:

yarn add @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-nullish-coalescing-operator --dev
复制代码

安装完成后不要忘记在 babel 配置中启用:

{
  "plugins": ["@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-nullish-coalescing-operator"]
}
复制代码

Babel 转义

顺便看一下 babel 是如何转义 Optional chainingNullish coalescing 的。

Optional chaining 的转义

a?.b?.[c]?.()
delete a?.b?.c
// babel 转义后 =====> 
"use strict";
var _a, _a$b, _a$b$c, _a2, _a2$b;
(_a = a) === null || _a === void 0 ? void 0 : (_a$b = _a.b) === null || _a$b === void 0 ? void 0 : (_a$b$c = _a$b[c]) === null || _a$b$c === void 0 ? void 0 : _a$b$c.call(_a$b);
(_a2 = a) === null || _a2 === void 0 ? true : (_a2$b = _a2.b) === null || _a2$b === void 0 ? true : delete _a2$b.c;
复制代码

细心的同窗能够发现有几个比较值得注意的点。

注意点

  1. babel 会在每次属性取值时将属性值进行缓存而不是像平时代码中常写的直接 a && a.b && a.b.c,这是为了保证和原生实现的一致性,保证每一个属性取值只会取一次,避免在一些 getter 属性获取时形成取值次数不一致差别性。
  2. babel 在判断值是否为空时并无直接使用 == null 而是使用了比较繁琐的 === null || === void 0,这个主要是为了兼容 document.all,关于 document.all 写在后面。

Nullish coalescing 的转义

a ?? b
// babel 转义后 =====> 
"use strict";
var _a;
(_a = a) !== null && _a !== void 0 ? _a : b;
复制代码

能够注意到一样是为了兼容 document.allNullish coalescing 也使用了 === null || === void 0 来进行判断。

document.all

document.all 是一个比较奇怪的值,它是 document 中全部元素的集合,可是它是一个假值,而且是一个特殊的空值。

document.all || 1 // 1
document.all == null // true
document.all === null // false
document.all === undefined // false
复制代码

document.all 是一个残留的属性,这些特性也是为了一些之前的兼容考虑。HTML5 中已经将它废弃,能够不作过多了解。有兴趣的能够看下 MDN Document.all 的文档。

loose

因为 document.all 是一个废弃的属性,现实开发中其实不会遇到使用的场景,既然如此咱们就不必由于一个废弃的属性而致使 babel 转义出大量无心义代码。 咱们能够经过启用插件的 loose 属性来实现:

{
  "plugins": [["@babel/plugin-proposal-optional-chaining", {"loose": false}], ["@babel/plugin-proposal-nullish-coalescing-operator", {"loose": false}]]
}
复制代码

若是是 preset-env 中集成的更简单,直接将 preset-env 中的 loose 设为 true 就好了。

再看下编译的代码:

a?.b?.[c]?.()
// babel 转义后 =====> 
"use strict";
var _a, _a$b, _a$b$c;
(_a = a) == null ? void 0 : (_a$b = _a.b) == null ? void 0 : (_a$b$c = _a$b[c]) == null ? void 0 : _a$b$c.call(_a$b);
复制代码
a ?? b
// babel 转义后 =====> 
"use strict";
var _a;
(_a = a) != null ? _a : b;
复制代码

代码瞬间简洁了许多。若是小心团队有同窗误用 document.all 也能够在 eslint 中添加告警。

总结

Optional chainingNullish coalescing 已经归入标准一段时间了,使用后能够大大增长属性获取、默认值设置等代码的优雅、可读性,减小各类 Cannot read property 'foo' of undefined 的错误状况。等什么,赶忙用起来吧。

若是以为本文有用,请不要吝啬您的赞👍

相关文章
相关标签/搜索