在1月的TC39议会确定下了ECMAScript 2018的功能集
1.关于ECMAScript版本
自TC39开始,ECMAScript版本的重要性大大降低,重要的是提议的特性处于什么样的阶段。只要进入第四阶段就可以安全使用。即便如此,你在使用的时候最好还是检查其兼容性
2.ES2018新特性
主要特性
异步函数迭代器
几个新概念和实体
- 两个新接口
AsyncIterable
和AsyncIterator
- 新的常用对象
AsyncGenerator
,AsyncFromSyncIteratorPrototype
,AsyncGeneratorFunction
,AsyncGeneratorPrototype
,AsyncIteratorPrototype
- 一个新的Symbol
Symbol.asyncIterator
这部分涉及的比较多,具体内容参考:http://2ality.com/2016/10/asynchronous-iteration.html,后面再单独整理一份。
展开/剩余(rest/spread) 扩展特性
rest操作符用于对象结构赋值
// 用于数组
const [...iterableObj] = [1, 3, 5, 7, 9];
[...iterableObj, 0, 2, 4, 6, 8];
// output
// [1, 3, 5, 7, 9, 0, 2, 4, 6, 8]
// 用于对象
const obj = {foo: 1, bar: 2, baz: 3};
const {foo, ...rest} = obj;
// output foo rest
// {foo:1}
// {bar: 2, baz: 3}
rest操作符也能收集其他参数
function func({param1, param2, ...rest}) { // rest operator
console.log('All parameters: ',
{param1, param2, ...rest}); // spread operator
return param1 + param2;
}
语法限制:结构时在对象的顶层只能使用一个...rest操作符并且要放在最后
const {...rest, foo} = obj; // SyntaxError const {foo, ...rest1, ...rest2} = obj; // SyntaxError
spread操作符能够将所有的可枚举属性插入到新的对象中
> const obj = {foo: 1, bar: 2, baz: 3};
> {...obj, qux: 4}
{ foo: 1, bar: 2, baz: 3, qux: 4 }
...rest和...spread常用在克隆对象,合并操作等场景。如填充一些默认属性
const DEFAULTS = {foo: 'a', bar: 'b'};
const userData = {foo: 1};
const data = {...DEFAULTS, ...userData};
// {foo: 1, bar: 'b'}
正则新特性
定义常量组名
在此之前,所有的分组是通过数字来实现的:第一组matchObj[1],第二组matchObj[2] etc
const RE_DATE = /([0-9]{4})-([0-9]{2})-([0-9]{2})/;
const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31
通过数字分组有几个缺点:
- 你要找到每组对应的结果很麻烦得去计算有几个圆括号
- 如果你想知道每个组的意义你得去看正则表达式
- 如果你想改变顺序还必须改正则表达式
现在,这些问题可以通过定义常量组名的方式来解决
通过名称来识别组名
(?<year>[0-9]{4})
此处声明了一个year的组名,匹配成功之后我们就可以通过matchObj.groups.year
来访问
const RE_DATE = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;
const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31
结合解构
const {groups: {day, year}} = RE_DATE.exec('1999-12-31');
console.log(year); // 1999
console.log(day); // 31
定义组名有几个好处:
- 找到组的“ID”更容易。
- 匹配的代码变得自描述,因为组的ID描述了捕获的内容
- 如果更改捕获组的顺序,则不必更改匹配的代码
- 捕获组的名称也使正则表达式更容易理解,因为您可以直接看到每个组的用途
利用replace实现格式化
console.log('1999-12-31'.replace(RE_DATE,
(...args) => {
const {year, month, day} = args[args.length-1];
return month+'/'+day+'/'+year;
}));
// 12/31/1999
这里的args包含a,y,m,d,groups
,这里的groups是args的最后一个参数,我们通过结构的方式访问{year, month, day}
等同于:
console.log('1999-12-31'.replace(RE_DATE,
(a,y,m,d, {year, month, day}) => month+'/'+day+'/'+year)); // (A)
// 12/31/1999
Unicode转义特性
目前js可以通过字符集的名称来匹配字符。如s代表空白
> /^\s+$/u.test('\t \n\r')
true
现在提议之后将可以实现通过Unicode字符属性来匹配字符。如
// 空白
> /^\p{White_Space}+$/u.test('\t \n\r')
true
// 希腊字母
> /^\p{Script=Greek}+$/u.test('μετά')
true
// 匹配拉丁字母
> /^\p{Script=Latin}+$/u.test('Grüße')
true
// 匹配单独的替代字符
> /^\p{Surrogate}+$/u.test('\u{D83D}')
true
后向断言
前向断言的意思的与当前位置之后的内容匹配
在前向断言中接下来的任何东西都必须与断言匹配,但是没有其他事情发生。也就是说,没有任何东西被捕获,并且断言对整个匹配的字符串没有贡献。
const RE_AS_BS = /aa(?=bb)/;
这个表达式匹配的范围是‘aabb’,a后面必须是两个b才能够被匹配,但是结果并不包含b
后向断言则与前向相反,是与当前位置之前的内容匹配。如
const RE_DOLLAR_PREFIX = /(?<=\$)foo/g;
'$foo %foo foo'.replace(RE_DOLLAR_PREFIX, 'bar');
// '$bar %foo foo'
正如你所看到的,‘foo’只会在前面有一个‘$’符的时候才会被匹配
s(dotAll)
目前正则表达式的.
有两个限制。首先,它不符合非BMP字符,如表情符号
> /^.$/.test('')
false
当然,在目前的提议下可以同过Unicode来实现
> /^.$/u.test('')
true
其次,点不匹配行结束符
> /^.$/.test('\n')
false
只能通过[^]
或者[\s\S]
来修复
> /^[^]$/.test('\n')
true
> /^[\s\S]$/.test('\n')
true
建议之后引入了新的修饰符/s
(简称:singleline)能够匹配行结束符
> /^.$/s.test('\n')
true
其他
Promise.prototype.finally()
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
finally
回掉会一直执行;then
回掉仅仅是在promisefulfilled
的时候执行;catch
毁掉仅仅是在promiserejected
或者then
回掉抛异常的时候执行。常见的场景如:
let connection;
db.open()
.then(conn => {
connection = conn;
return connection.select({ name: 'Jane' });
})
.then(result => {
// Process result
// Use `connection` to make more queries
})
···
.catch(error => {
// handle errors
})
.finally(() => {
connection.close();
});
模板字符串修正
该建议是为了让标签模板中的语法更加自由。String.raw() 是一个模板字符串的标签函数,它的作用类似于 Python 中的字符串前缀 r
和 C# 中的字符串前缀 @
,是用来获取一个模板字符串的原始字面量值的。在接受模板字符串的时候会解析为两个版本:
- Cooked: 转义字符是被解释。
\u{4B}
becomes'K'
- Raw: 转义字符是原始文本。
\u{4B}
becomes'\\u{4B}'
即使使用Raw版本,你也没有太多的自由。因为多了反斜杠之后就不合法了:
-
\u
开始是一个Unicode字符,之后变成\u{1F4A4}
或者\u004B
-
\x
开始是一个16进制字符,之后变成了\x4B
解决方法是放弃与转义序列相关的所有语法限制。然后,非法转义序列只是在Raw表示中逐字显示。具有非法转义序列的每个模板字符串在Cooked数组中都是未定义的元素:
{ Cooked: [ undefined, undefined ], Raw: [ '\\uu ', ' \\xx' ] }
参考
https://github.com/tc39/proposals/blob/master/finished-proposals.md