ECMAScript2018新特性一览

本文介绍了ECMAScript 2018的主要更新,包括异步函数迭代器、展开/剩余扩展特性、正则新特性、Promise.prototype.finally()等。同时探讨了这些新特性如何改善JavaScript开发体验。

在1月的TC39议会确定下了ECMAScript 2018的功能集

1.关于ECMAScript版本

TC39开始,ECMAScript版本的重要性大大降低,重要的是提议的特性处于什么样的阶段。只要进入第四阶段就可以安全使用。即便如此,你在使用的时候最好还是检查其兼容性

2.ES2018新特性

主要特性
异步函数迭代器

几个新概念和实体

  • 两个新接口AsyncIterableAsyncIterator
  • 新的常用对象AsyncGenerator,AsyncFromSyncIteratorPrototype,AsyncGeneratorFunction,AsyncGeneratorPrototype,AsyncIteratorPrototype
  • 一个新的SymbolSymbol.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

通过数字分组有几个缺点:

  1. 你要找到每组对应的结果很麻烦得去计算有几个圆括号
  2. 如果你想知道每个组的意义你得去看正则表达式
  3. 如果你想改变顺序还必须改正则表达式

现在,这些问题可以通过定义常量组名的方式来解决

通过名称来识别组名

(?<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

定义组名有几个好处:

  1. 找到组的“ID”更容易。
  2. 匹配的代码变得自描述,因为组的ID描述了捕获的内容
  3. 如果更改捕获组的顺序,则不必更改匹配的代码
  4. 捕获组的名称也使正则表达式更容易理解,因为您可以直接看到每个组的用途

利用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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值