ES 提案:JavaScript 的空值合并(nullish coalescing)
这篇博客文章描述 Gabriel Isenberg 提出的 ECMAScript 功能提案“Nullish coalescing for JavaScript”。它提出使用 ??
操作符替代 ||
来提供默认值。
概述
实际值的 ??
操作结果取默认值的情况是:
- 如果值是
null
或undefined
时取默认值。 - 其它情况就是值本身。
举例:
undefined ?? 'default' // 'default'
null ?? 'default' // 'default'
false ?? 'default' // false
'' ?? 'default' // ''
0 ?? 'default' // 0
以前:通过 || 获取默认值
下面的两个表达式是等价的,总结文章 the logical Or operator || 查看更多。
a || b
// eslint-disable-next-line no-unneeded-ternary
a ? a : b
意思是:
- 如果
a
是真值(truthy
[1]),那么a || b
的结果是a
- 其它情况结果为
b
这使得可以使用 ||
指定在实际值为假值(falsy
[2]) 时需要使用的默认值:
const result = actualValue || defaultValue
function getTitle(fileDesc) {
return fileDesc.title || '(Untitled)'
}
const files = [{ path: 'index.html', title: 'Home' }, { path: 'tmp.html' }]
console.log(files.map(f => getTitle(f)))
// => ['Home', '(Untitled)']
注意,只有实际值是 null
或 undefined
时才使用默认值。需要这样,是因为 null
和 undefined
都为假值(falsy
):
undefined || 'default' // 'default'
null || 'default' // 'default'
并且,当实际值是任何的假值(falsy
)时默认值也会被使用,举例:
false || 'default' // 'default'
'' || 'default' // 'default'
0 || 'default' // 'default'
于是,代码 getTitle()
不能正确运行:
getTitle({ path: 'empty.html', title: '' }) === '(Untitled)'
空值合并操作符 ??
当要提供默认值时,空值合并操作符 ??
用来替换逻辑运算符和 ||
操作符。下面两个表达式是等价的:
a ?? b
a !== undefined && a !== null ? a : b
默认值像这样使用:
const result = actualValue ?? defaultValue
对于 undefined
和 null
,??
操作符与 ||
操作符结果相同:
undefined ?? 'default' // 'default'
null ?? 'default' // 'default'
然而,对于其它左侧的操作数,它不会返回默认值,即使它们的值是假值(falsy
):
false ?? 'default' // false
'' ?? 'default' // ''
0 ?? 'default' // 0
让我们使用 ??
重写 getTitle()
:
function getTitle(fileDesc) {
return fileDesc.title ?? '(Untitled)'
}
现在运行它获取 .title
是期望的那样返回空字符串:
getTitle({ path: 'empty.html', title: '' }) === ''
通过解构获取默认值
getTitle()
使用 ??
操作符简单方法来实现。注意,你也可以使用解构实现它:
function getTitle({ title = '(Untitled)' }) {
return title
}
使用 ?? 操作符的真实例子
一个真实例子,我们使用 ??
简化接下来的方法。字符串方法 .match()
的说明文章“JavaScript for impatient programmers”。
function countMatches(regex, str) {
if (!regex.global) {
throw new Error(`Regular expression must have flag /g: ${regex}`)
}
const matchResult = str.match(regex) // null or Array
if (matchResult === null) {
return 0
} else {
return matchResult.length
}
}
console.log(countMatches(/a/g, 'ababa')) // 3
console.log(countMatches(/b/g, 'ababa')) // 2
console.log(countMatches(/x/g, 'ababa')) // 0
countMatches(/a/, 'ababa') // Error
如果使用 ??
操作符,代码如下:
function countMatches(regex, str) {
if (!regex.global) {
throw new Error(`Regular expression must have flag /g: ${regex}`)
}
return (str.match(regex) ?? []).length
}
空值合并和可选链(optional chaining)
空值合并操作符 ??
明确设计配合属性访问的可选链。举例来说,在下面的代码中,它们两个在行 A 中都使用了。
const persons = [
{
surname: 'Zoe',
address: {
street: {
name: 'Sesame Street',
number: '123',
},
},
},
{
surname: 'Mariner',
},
{
surname: 'Carmen',
address: {},
},
]
const streetNames = persons.map(p => p.address?.street?.name ?? '(no name)') // (A)
console.log(streetNames)
// ["Sesame Street", "(no name)", "(no name)"]
实践
- 可以检查 Can I use 查看
??
操作符的支持情况。 - babel 插件支持 @babel/plugin-proposal-nullish-coalescing-operator。
扩展阅读
- 博客文章“ES proposal: optional chaining”
- Section “Logical Or (||)” in “JavaScript for impatient programmers”
- Chapter “Destructuring” in “JavaScript for impatient programmers”
译者注
[1] truthy
不是 true
,详见 MDN 的解释。
[2] falsy
不是 false
,详见 MDN 的解释。
本文由 吳文俊 翻译,原文地址 ES2020: Nullish coalescing for JavaScript