为了保证的可读性,本文采用意译而非直译。
想阅读更多优质文章请猛戳 GitHub 博客, 一年百来篇优质文章等着你!
这个礼拜《大迁世界》有抽奖活动,奖品:专栏《左耳听风》x3, 技术书 x5, 欢迎关注回复:抽 奖
本文主要讲 Gabriel Isenberg 撰写的 ES 提案“Nullish coalescing for JavaScript”。它提出 ??
替换||
的运算符, 并提供默认值。这里先把这相提案叫作 双问号操作符,如果你有好的叫法,欢迎留言讨论。
1. 概述
双问号 ??
的操作符跟 ||
类似,如果给定变量值为 null
或者 undefined
,刚使用双问号后的默认值,否则使用该变量值。
如下:
> undefined ?? 'default'
'default'
> null ?? 'default'
'default'
> false ?? 'default'
false
> ''??'default'''
> 0 ?? 'default'
0
2. 早期的 || 运算符号
直接来个例子来演示一下 ||
运算,下面两个等式是等价的:
a || b
a ? a : b
如果 a
是 truthy 值,则返回 a
, 否则返回 b
。
这使得使用 ||
指定一个默认值成为可能,如果实际值是假的,那么将使用这个默认值:
const result = actualValue || defaultValue;
function getTitle(fileDesc) {return fileDesc.title || '(Untitled)';
}
const files = [{path: 'index.html', title: 'Home'},
{path: 'tmp.html'},
];
assert.deepEqual(files.map(f => getTitle(f)),
['Home', '(Untitled)']);
请注意,基本只有在实际值 undefined
或为 null
时才应使用默认值,这是有效的,因为 undefined
和null
都是假 (虚值) 的:
> undefined || 'default'
'default'
> null || 'default'
'default'
遗憾的是,如果实际值是其他的虚值,也会使用默认值:
> false || 'default'
'default'
> ''||'default''default'
> 0 || 'default'
'default'
因此,这个 getTitle()
并不总能正常工作:
assert.equal(getTitle({path: 'empty.html', title: ''}),'(Untitled)');
3. 使用双问号操作符来解决 || 运算的问题
??
主要是用来解决 ||
操作符号的一些问题,以下两个表达式是等价的:
a ?? b
a !== undefined && a !== null ? a : b
默认值是这样提供的:
const result = actualValue ?? defaultValue;
对于 undefined
和null
,??
操作符的工作原理与 ||
操作符相同
> undefined ?? 'default'
'default'
> null ?? 'default'
'default'
除了 undefined
和 null
的其它虚值,??
不会返回默认值。
> false ?? 'default'
false
> ''??'default'''
> 0 ?? 'default'
0
使用 ??
来重写 getTitle()
:
function getTitle(fileDesc) {return fileDesc.title ?? '(Untitled)';
}
现在使用 fileDesc
调用它,它的 .title
是空字符串,仍然可以按符合咱们的预期工作:
assert.equal(getTitle({path: 'empty.html', title: ''}),'');
3.1 通过解构给定默认值
除了使用 ??
给 getTitle
添加默认值,咱们也可以通过解构方式来给定默认值:
function getTitle({title = '(Untitled)'}) {return title;}
3.2 使用 ?? 操作符号的实际例子
作为一个现实的例子,咱们使用 ??
来简化下面的函数。
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;}
}
assert.equal(countMatches(/a/g, 'ababa'), 3);
assert.equal(countMatches(/b/g, 'ababa'), 2);
assert.equal(countMatches(/x/g, 'ababa'), 0);
// Flag /g is missing
assert.throws(() => 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;
}
3.3 双问号 (??) 操作符与可选链(?)
双问号 (??
) 的提出是为了补充可选链(?
),来看看这两兄弟结合使用的场景(第 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)
assert.deepEqual(streetNames, ['Sesame Street', '(no name)', '(no name)']
);
代码部署后可能存在的 BUG 没法实时知道,事后为了解决这些 BUG,花了大量的时间进行 log 调试,这边顺便给大家推荐一个好用的 BUG 监控工具 Fundebug。
4. 兼容性
可以通过 ECMAScript Next compatibility table 查看 ??
支持情况。
交流
干货系列文章汇总如下,觉得不错点个 Star,欢迎 加群 互相学习。
https://github.com/qq44924588…
我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复 福利,即可看到福利,你懂的。