最近工做中发现团队有些同窗不太了解
Optional chaining
和Nullish coalescing
两个新的操做符,正好推广一波shell
Optional chaining
和 Nullish coalescing
目前都已经归入 ECMA-262
标准中,不过兼容性还差得远,以下: json
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
操做符的定义是:当左操做数为空值时(null
、undefined
)中断取值操做并返回 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.b
cdn
Nullish coalescing
介绍再来看看 Nullish coalescing
,Nullish coalescing
和 Optional chaining
算是一对好基友,主要用来作一些默认值的设置。
Nullish coalescing
操做符的定义是:当左操做数为空值时(null
、undefined
)返回右操做数,不然返回左操做数。
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
是如何转义 Optional chaining
和 Nullish 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;
复制代码
细心的同窗能够发现有几个比较值得注意的点。
babel
会在每次属性取值时将属性值进行缓存而不是像平时代码中常写的直接 a && a.b && a.b.c
,这是为了保证和原生实现的一致性,保证每一个属性取值只会取一次,避免在一些 getter 属性获取时形成取值次数不一致差别性。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.all
,Nullish 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 chaining
和 Nullish coalescing
已经归入标准一段时间了,使用后能够大大增长属性获取、默认值设置等代码的优雅、可读性,减小各类 Cannot read property 'foo' of undefined
的错误状况。等什么,赶忙用起来吧。
若是以为本文有用,请不要吝啬您的赞👍