ES2018增加的新特性不多。
新语法
剩余参数自动结构推广至对象字面量
...
这个神奇的操作符现在已经推广到对象字面量,也就是已经形成了“...
全家桶”了:
'use strict';
const { a, ...rest } = { a: 1, b: 2, c: 3 };
alert(a === 1 && rest.b === 2 && rest.c === 3);
// 不过要注意,对象字面量的剩余解构不支持数组的那种嵌套,下面的写法是语法错误
// const { a, ...{ b, ...rest } } = { a: 1, b: 2, c: 3, d: 4 };
const obj = { a, ...rest }; // 也可以反向操作
alert(obj.a === 1 && obj.b === 2 && obj.c === 3);
若将...
作用在其它非字面量对象实例,则只可以对那些可枚举的属性进行自动解构/扩散。
更完整的正则表达式功能
JS一直以来的正则表达式都不支持命名捕获等一些新功能,现在总算补上了:
'use strict';
// 命名分组捕获的语法为“(?<分组名>...正则表达式...)”
const dateRegExp = /(?<year>\d+)-(?<month>\d{1,2})-(?<day>\d{1,2})/;
const dateResult = dateRegExp.exec('2019-11-30'),
dateGroups = dateResult.groups; // 命名捕获都在新增的分组属性中
alert(dateGroups.year === '2019' &&
dateGroups.month === '11' &&
dateGroups.day === '30' &&
// 旧语法的分组结果
dateResult[0] === '2019-11-30' &&
dateResult[1] === '2019' &&
dateResult[2] === '11' &&
dateResult[3] === '30');
alert(/123.456/.test(`123
456`)); // 旧语法不能用“.”匹配换行
alert(/123.456/s.test(`123
456`)); // 新增的“s”模式则可以
// ES5已经引入了展望(Lookahead)断言
alert(JSON.stringify('123'.match(/1(?=23)/g))); // 展望正向
alert(JSON.stringify('231'.match(/1(?=23)/g)));
alert(JSON.stringify('231'.match(/1(?!23)/g))); // 展望反向
alert(JSON.stringify('123'.match(/1(?!23)/g)));
// ES2018则引入了回溯(Lookbehind)断言
alert(JSON.stringify('123'.match(/(?<=23)1/g))); // 回溯正向
alert(JSON.stringify('231'.match(/(?<=23)1/g)));
alert(JSON.stringify('123'.match(/(?<!23)1/g))); // 回溯反向
alert(JSON.stringify('231'.match(/(?<!23)1/g)));
// Unicode模式添加了“\p{key=value}”的属性配置
// 下方就是使用希腊字母进行匹配的配置
alert(/\p{Script=Greek}/u.test('π'));
async推广至生成器及其迭代器
async
推广至生成器是顺理成章的,相当于原本生成器的迭代器每次迭代出来的元素都被封装在Promise
里:
'use strict';
async function *g() {
yield 1;
yield 2;
yield 3;
}
(async () => { // 记住,await只能在async内使用
// 异步生成器需要配套新的for..await..of循环来使用
for await(const v of g())
alert(v);
})().finally(() => {
// 也可以配套“迭代器+Promise”的编程模型来使用
const itr = g(),
goOn = (el) => {
if (!el.done) {
alert(el.value);
itr.next().then(goOn);
}
};
itr.next().then(goOn);
// 异步生成器的迭代器每次next()都会返回一个Promise,即便生成器已经走到尽头
// 所以在循环中使用它的时候要注意这个细节,否则很容易造成死循环
/* for (const itr = g(), p = itr.next();
p != null; // 这样的判断将无法有效跳出循环
p = itr.next()) { ... } */
});
新API
Promise的finally()方法
在ES2015带来Promise
的时候只有then()
和catch()
两个实例方法,而在ES2018中则添加了finally()
方法,这样Promise
的接口总算完整了(虽然我早已经使用着这个新方法了)。注意这个新方法无法改变resovle
回调的结果,但可以改变reject
回调的结果,就正如在try...catch...finally
代码块中,finally
再抛出异常的话相当于整体代码块任何一处抛出异常是等价的,而try
中已经return
了结果的话finally
无论怎样都不能覆盖前面的结果。
'use strict';
Promise.resolve(111)
.finally(() => {
return Promise.resolve(222); // 尝试覆盖
})
.then(result => {
alert(result); // 但不会成功
});
Promise.reject(111)
.finally(() => {
return Promise.reject(222); // 尝试覆盖
})
.catch(result => {
alert(result); // 这样会成功
});
Proxy的ownKeys()拦截器返回重复键时将会报错
这个我以为是理所当然的,但事实上要到了ES2018的规范才会报错,而之前的都不会……
'use strict';
const p = new Proxy({}, {
ownKeys() {
return ['a', 'b', 'a'];
}
});
try {
Reflect.ownKeys(p);
} catch (e) {
alert(e);
}
打赏作者