JavaScript 中强大的可选链
- 写在前面
- 可选链
- 可选链 de 三种形式
- 短路:遇到 null/undefined 停止
- 细节拉满
- 注意事项
- 兼容性
写在前面
当你在获取一个对象的属性的时候,你可能需要检测这个属性是否存在,否则会报错;比如:
var obj
console.log(obj.name)
会报错:TypeError: Cannot read property 'name' of undefined
;
特别是后端接口返回的数据,前端数据一般是你自己定义的,有就是有,没有就是没有;
后端有时候说好的有,结果没有…/(ㄒoㄒ)/~~
开个玩笑,继续
所以在使用接口返回的数据的时候,要做属性判断,再使用:
if(resp && resp.data) {
this.tableData = resp.data
}
如果属性嵌套很深,就很难受了 ≧ ﹏ ≦
if (person && person.owner && person.owner.permission) {
let usecar = person.owner.permission.usecar
}
第三方工具库像 lodash
等,提供了类似这种快捷的判断取值:
// <script src="lodash.js"></script>
_.get(person, 'owner.permission.usecar');
也能够解决对应的问题,但不是本文讨论的问题,重点在下面。
⬇
可选链
对于复杂的属性判断,使用 可选链 就很棒了。
它使我们能检查一个对象上面的某属性是否为 null
或者 undefined
,如果是,则返回 undefined
,而不会报错。
this.tableData = resp?.data
可选链还可以在数组中使用
let moveName = movie.actors?.[0]?.name
⬇
可选链 de 三种形式
object?.property
;常见的一种object?.[expression]
;用于访问数组项或访问动态属性object?,[arg1, [arg2, ...]
;用于执行对象的一个方法,有时候不知道这个对象的方法是否存在,也可以用可选链来搞
const object = null
object?.method('Some value') // => undefined
短路:遇到 null/undefined 停止
可选链接运算符的有趣之处在于,只要在左侧 leftHandSide?.rightHandSide
遇到无效值,右侧访问就会停止,这称为短路。
看看例子:
const nothing = null;
let index = 0;
nothing?.[index++]; // => undefined
index; // => 0
细节拉满
对于不存在的属性,可选链返回的是 undefined
,undefined
是不太友好的,在代码中会存在问题;我们希望当属性不存在的时候,给一个默认值。
但是如果使用三目运算符,好像有点僵硬
// name不存在的时候,用 - 代替
let moveName = movie.actors?.[0]?.name ? movie.actors?.[0]?.name : '-'
一个新的提议【双问号】 nullish coalescing operator , 处理 undefined
或 null
的时候,可以给它们默认为特定值。
语法: 变量名 ??
const noValue = undefined;
const value = 'Hello';
noValue ?? 'no value'; // => 'no value'
value ?? 'no value'; // => 'Hello'
【细节拉满】
// name不存在的时候,用 - 代替
let moveName = movie.actors?.[0]?.name ?? '-'
注意事项
① 避免过度使用
// 连续多次判断同一属性,不好
console.log(foo?.bar)
console.log(foo?.baz)
// 还不如
if(foo) {
console.log(foo.bar)
console.log(foo.baz)
}
② 省去赋值判断
// before
if (foo && foo.bar) {
foo.bar.baz = someValue;
}
// after
foo?.bar?.baz = someValue
赋值的时候,就不要这样用了;这样运行是会报错的,你可以试一下。
③ ?.
一般使用在可能为空的属性:maybeNullish?.prop
。在确定属性不为空的情况下,使用属性访问器:.property
或 [propExpression]
即可。
// not good
function logMovie(movie) {
// movie doesn't need optional chaining
console.log(movie?.title);
}
④有时,你可能有更好的选择
function hasPadding({ padding }) {
const top = padding?.top ?? 0;
const right = padding?.right ?? 0;
const bottom = padding?.bottom ?? 0;
const left = padding?.left ?? 0;
return left + top + right + bottom !== 0;
}
// 还不如
function hasPadding({ padding }) {
const p = {
top: 0,
right: 0,
bottom: 0,
left: 0,
...padding
};
return p.top + p.left + p.right + p.bottom !== 0;
}
这个就比可选链来的更简洁。
兼容性
在安装了 babel
的情况下,可选链的兼容性还是 OK 的;但是 babel
可能还并不支持 双问号 的语法。(你可以直接尝试一下这个语法,如果不生效,在网上找解决方案)