一年多过去了, Optional Chaining
终于到Stage 3
了, vscode 之类编辑器也有办法支持该语法了, 现在想用, 可以用babel
来实现了
安装插件
# yarn add @babel/plugin-proposal-optional-chaining --dev // 可选链
# yan add @babel/plugin-proposal-nullish-coalescing-operator --dev // 空值合并
在 babel 配置文件里添加插件
plugins: [
// 其他插件
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-nullish-coalescing-operator'
]
这样就可以在 js, jsx, vue 里面用了
在 .jsx 中使用:
render() {
const { data } = this.props.article
const age = null
return (
<div>
{data?.author?.loginname}
年龄: {age ?? 18}
</div>
)
}
在 .js 中使用:
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah',
age: undefined,
num: 0
}
}
const dogName = adventurer.dog?.name;
console.log(dogName);
// 打印: undefined
console.log(adventurer?.cat?.age ?? 18)
// 打印: 18
console.log(adventurer?.cat?.num ?? 1)
// 打印: 0
在 .vue 中使用:
注意, 在.vue
中, 只能在script
标签里使用, 不能在template
中使用, 因为template
里的 js 表达式是不会经过babel
编译的(至少到目前为止的 vue2 版本都不行, 也许 vue3 就可以了), 所以除非浏览器原生支持, 不然就会报错
在template
中可以借助lodash.get
和过滤器来写, 这样看起来会优雅些
<template>
<div>
<p>{{ obj | oc('a.b.c') }}</p>
<p>{{ obj | oc('a.c.d') }}</p>
</div>
</template>
<script>
import get from 'lodash.get'
export default {
filters: {
oc(obj, path) {
if (!obj) return null
if (!path) return obj
return get(obj, path)
}
},
data() {
return {
obj: {
a: {
b: {
c: 'mmxiaowu'
}
}
}
}
}
}
</script>
华丽的分割线
Optional Chaining
是个好东西, 可惜现在还处于Stage 1
阶段, 虽然 babel 已经实现了该功能, 但是编辑器, eslint 之类还是不支持, 想用还是遥遥无期
Optional Chaining
的作用, 主要是让开发者告别一堆的&&
:
const obj = {
abc: {
def: {
ghi: '123'
}
}
}
上面的对象, 如果想取最后的123
, 在不确定前面 abc, def 是否为 null, undefined 的情况下, 我们就得这么写:
const str = obj && obj.abc && obj.abc.def && obj.abc.def.ghi
天长地久的一串, 但是有Optional Chaining
之后, 你只需要这么写:
const str = obj?.abc?.def?.ghi
是不是简单了很多...
既然Optional Chaining
还用不了, 就自己写个简单的曲线救国的方法:
const oc = (props, property, def) => {
// 如果props不是对象, 则直接返回原值
if (Object.prototype.toString.call(props) !== '[object Object]') return props
// 如果property不是字符串, 则直接返回原值
if (!property || typeof property !== 'string') return props
const arrProperty = property.split('.')
const $return = arrProperty.reduce((prev, curr) => {
if (prev === null || prev === undefined) return
if (Object.prototype.toString.call(prev) === '[object Object]') return prev[curr]
}, props)
return def && $return === undefined ? def : $return
}
var obj = {
abc: {
def: {
ghi: '123'
}
}
}
oc(obj, 'abc')
// 输出:
/*
{
def: {
ghi: '123'
}
}
*/
oc(obj, 'xyz') // 输出 undefined
oc(obj, 'abc.def')
// 输出:
/*
{
ghi: '123'
}
*/
oc(obj, 'abc.def.ghi') // 输出 '123'
var obj = {
abc: {
def: [{
ghi: '123'
}]
}
}
oc(obj, 'abc.def.ghi') // 输出 undefined
oc(obj, 'abc.def.ghi', {}) // 输出 {}
oc(obj, 'abc.def.0.ghi') // 输出 '123'
oc(obj, 'abc.def.length') // 输出 1
var a = {
b: null,
c: {
d: 1
},
d: 0
}
console.log(oc(a, 'b')) // null
console.log(oc(a, 'b.c')) // undefined
console.log(oc(a, 'c.d')) // 1
console.log(oc(a, 'c.d.e')) // undefined
console.log(oc(a, 'd')) // 0