JavsScript ES9&ES2018的新特性

写在前面的话标准非实际情况。

ECMAScript 2018,ECMA-262 标准版本的第9版(通常称为ES2018或ES9),于 2018 年 6 月完成。

ECMAScript 2018 introduced support for asynchronous iteration via the AsyncIterator protocol and async generators. It also included four new regular expression features: the dotAll flag, named capture groups, Unicode property escapes, and look-behind assertions. Lastly it included rest parameter and spread operator support for object properties.

  • Rest(剩余)/Spread(展开) 属性
  • Asynchronous iteration (异步迭代)
  • Promise.prototype.finally()
  • 正则表达式改进
    • 先行断言(lookahead) 和 后行断言(lookbehind)
    • Unicode 属性转义 \p{…} 和 \P{…}
    • 命名捕获组(Named capture groups)
    • 正则表达式的 ‘s’ 标志

Rest(剩余) 属性

对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。但是是浅拷贝。

1
2
3
4
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }

解构赋值必须是最后一个参数,而且等号右边是 undefinednull都会报错

>>查看更多信息<<

Spread(展开) 属性

对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。等同于使用Object.assign()方法。

1
2
3
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

>>查看更多信息<<

Asynchronous iteration (异步迭代)

for...of循环用于遍历同步的 Iterator 接口。新引入的for await...of循环,则是用于遍历异步的 Iterator 接口。

1
2
3
4
5
6
7
async function f() {
for await (const x of createAsyncIterable(['a', 'b'])) {
console.log(x);
}
}
// a
// b

上面代码中,createAsyncIterable()返回一个拥有异步遍历器接口的对象,for…of循环自动调用这个对象的异步遍历器的next方法,会得到一个 Promise 对象。await用来处理这个 Promise 对象,一旦resolve,就把得到的值(x)传入for…of的循环体。

>>查看更多信息<<

Promise.prototype.finally()

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。finally方法的回调函数不接受任何参数,因此finally方法里面的操作,应该是与状态无关的

1
2
3
4
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

finally的简单实现

1
2
3
4
5
6
7
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};

>>查看更多信息<<

正则表达式改进

先行断言(lookahead) 和 后行断言(lookbehind)

JavaScript 语言的正则表达式,只支持先行断言(lookahead)和先行否定断言(negative lookahead),该版本引入后行断言(lookbehind)和后行否定断言(negative lookbehind)。

  • “先行断言”指的是,x只有在y前面才匹配,必须写成/x(?=y)/
  • “先行否定断言”指的是,x只有不在y前面才匹配,必须写成/x(?!y)/
  • “后行断言”正好与“先行断言”相反,x只有在y后面才匹配,必须写成/(?<=y)x/
  • “后行否定断言”则与“先行否定断言”相反,x只有不在y后面才匹配,必须写成/(?<!y)x/

先行断言 先行否定断言

1
2
/\d+(?=%)/.exec('100% of US presidents have been male')  // ["100"]
/\d+(?!%)/.exec('that’s all 44 of them') // ["44"]

后行断言 后行否定断言

1
2
/(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill')  // ["100"]
/(?<!\$)\d+/.exec('its is worth about90') // ["90"]

>>查看更多信息<<

Unicode 属性转义 \p{…} 和 \P{…}

\p{...}允许正则表达式匹配符合 Unicode 某种属性的所有字符,\P{...}匹配不满足条件的字符。这两种类只对 Unicode 有效,所以使用的时候一定要加上u修饰符。如果不加u修饰符,正则表达式使用\p\P会报错。

匹配一个希腊文字母

1
2
const regexGreekSymbol = /\p{Script=Greek}/u;
regexGreekSymbol.test('π') // true

Unicode 属性类要指定属性名和属性值。

1
\p{UnicodePropertyName=UnicodePropertyValue}

对于某些属性,可以只写属性名,或者只写属性值。

1
2
\p{UnicodePropertyName}
\p{UnicodePropertyValue}

1
2
/^\p{Script=Greek}+$/u.test('ελληνικ?') // true
/^\p{Script=Latin}+$/u.test('hey') // true

具体有哪些属性和属性值可以提案上查看所有属性:tc39/proposal-regexp-unicode-property-escapes

>>查看更多信息<<

具名组匹配(Named capture groups)

允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。

1
2
3
4
5
6
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{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

具名组匹配等于为每一组匹配加上了 ID,便于描述匹配的目的。如果组的顺序变了,也不用改变匹配后的处理代码。同时,数字序号(matchObj[1])依然有效。

解构赋值

1
2
3
let {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
one // foo
two // bar

解构替换

1
2
3
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;

'2015-01-02'.replace(re, '$<day>/$<month>/$<year>') // '02/01/2015'

replace方法的第二个参数也可以是函数具体用法查看更多信息。

>>查看更多信息<<

正则表达式的 ‘s’ 修饰符

ES2018 引入s修饰符,使得.可以匹配任意单个字符。这被称为dotAll模式,即点(dot)代表一切字符。

1
2
/foo.bar/s.test('foo\nbar') // true
/foo.bar/s.test('fooabar') // true

>>查看更多信息<<

参考

ecma-262-9.0