【第1248期】ECMAScript 2016, 2017, 和2018中新增功能

222 阅读15分钟
原文链接: mp.weixin.qq.com

前言

你们的js语法好老旧,我们这都开始es7了。听到这样的化,扎心了没?落伍,落后了吗?今日早读文章由@hypers翻译分享。

正文从这开始~

很难跟踪JavaScript(ECMAScript)中的新功能,更难找到有用的代码示例。

因此,在本文中,我将介绍在ES2016,ES2017和ES2018(最终草案)中添加的TC39已完成提案中列出的所有18个功能,并向他们展示有用的示例。

这是一个相当长的帖子,但应该是一个简单的阅读。把这看作是“Netflix binge reading”。在这之后,我保证你将对这些功能有很多的了解。

让我们一个接一个地看看:

ECMAScript 2016

1. Array.prototype.includes

includes是数组上的简单实例方法,并有助于轻松查找某个项是否在Array中(包括NaN不像indexOf

const arr = [ 1, 2, 3, 4, NaN];// es2016不使用if(arr.indexOf(3) >= 0){  console.log(true);}//使用if(arr.includes(3)){  console.log(true);}//ps: 注意 indexOf 是不支持查找NaN的arr.includes(NaN) // truearr.indexOf(NaN) // -1 (indexOf 不支持 NaN)    

琐事:人们想要命名的JavaScript规范contains,但这显然已被Mootools使用,因此他们使用了includes

2. 指数运算符

数学运算如加法和减法分别有 + 和 -  等运算符。与他们类似,** 运算符通常用于指数运算。在ECMAScript 2016中,引入了 **  而不是Math.pow。

// 不使用Math.pow(7,2) //49//使用7**2 //49

ECMAScript 2017

1. Object.values()

Object.value()是一个与Object.keys()类似的新函数,但会返回Object自身属性的所有值,排除原型链中的任何值。

const cars = { 'BMW': 3, 'Tesla': 2, 'Toyota': 1 };//ES2015//ES2017 不使用const vals = Object.keys(cars).map(key => cars[key]);console.log(vals); //[3, 2, 1]//ES2017 and 未来//使用const values = Object.values(cars);console.log(values); // [3, 2, 1]
2. Object.entries()

Object.entries()Object.keys相关,但不是仅返回keys,而是以数组方式返回keys和values。这使得像循环中使用对象或将对象转换为Maps这样的事情变得非常简单。

例 1:

const cars = { 'BMW': 3, 'Tesla': 2, 'Toyota': 1 };// ES5.1//而不是提取键然后再循环Object.keys(cars).forEarch(function(key) {    console.log('key: ' + key + 'value: ' + cars[key]);})//ES2017 (ES8)//使用 Object.entries for(let [key value] of Object.entries(cars)) {    console.log(`key: ${key} value: ${value}`);}

例 2:

const cars = { 'BMW': 3, 'Tesla': 2, 'Toyota': 1 };//ES2015//不使用//获取对象键,然后添加每个项目以在循环中映射const maps = new Map();Object.keys(cars).forEarch(key => {    maps.set(key, cars[key]);})console.log(map1) // Map { 'BMW' => 3, 'Tesla' => 2, 'Toyota' => 1}//ES2017 and 以后//使用const map = new Map(Object.entries(cars));console.log(map) //Map { 'BMW' => 3, 'Tesla' => 2, 'Toyota' => 1}
3. String padding

向字符串 – String.prototype.padStartString.prototype.padEnd添加了两个实例方法 – 它们允许在原始字符串的开始或结尾附加/预先添加空字符串或其他字符串。

'someString'.padStart(numberOfCharcters [,stringForPadding]); '5'.padStart(10) // '          5''5'.padStart(10, '=*') //'=*=*=*=*=5''5'.padEnd(10) // '5         ''5'.padEnd(10, '=*') //'5=*=*=*=*='

当我们想要在诸如漂亮的打印显示或终端打印等场景中对齐事物时,这很方便。

3.1 padStart 例子

在下面的例子中,我们有一个不同长度的数字列表。我们希望预先设置“0”,以便所有项目具有相同长度的10位数字用于显示目的。我们可以使用padStart(10, '0')轻松实现这一点。

//ES2017//如果你有一个不同长度的项目列表,并希望格式化它们的显示目的,你可以使用padStartconst formatted = [0, 1, 12, 123, 1234, 12345].map(num =>     num.toString().padStart(10, '0') // 添加 0 直到长度为 10);console.log(formatted);//打印// [//     '0000000000',//     '0000000001',//     '0000000012',//     '0000000123',//     '0000001234',//     '0000012345',// ]    
3.2 padEnd 例子:

我们打印多个不同长度的项目并想要正确对齐它们时,padEnd真的很方便。

下面的例子是padEndpadStart和Object.entries如何聚合在一起产生漂亮输出的一个很好的实例。

const cars = {  '🚙BMW': '10',  '🚘Tesla': '5',  '🚖Lamborghini': '0'}Object.entries(cars).map(([name, count]) => {  //padEnd appends ' -' until the name becomes 20 characters  //padStart prepends '0' until the count becomes 3 characters.  console.log(`${name.padEnd(20, ' -')} Count: ${count.padStart(3, '0')}`)});//打印结果..// 🚙BMW - - - - - - -  Count: 010// 🚘Tesla - - - - - -  Count: 005// 🚖Lamborghini - - -  Count: 000
3.3 ⚠️ Emojis(表情符号)和其他双字节字符上的padStart和padEnd

Emojis和其他双字节字符使用unicode的多个字节表示。所以padStartpadEnd可能无法按预期工作!⚠️

例如:假设我们正在尝试用❤️表情符号填充字符串heart以达到10个字符。结果如下所示

//注意,不是5颗心,只有2颗心和1颗看起来很奇怪的心!'heart'.padStart(10, "❤️"); // prints.. '❤️❤️❤heart'

这是因为❤️长2个字符('\u2764\uFE0F')!单词heart本身是5个字符,所以我们只剩下5个字符。所以,JS 填塞两颗心用'\u2764\uFE0F'和生产❤️❤️。为最后一个只是用心灵的第一个字节 \ u2764产生❤

所以结果是: ❤️❤️❤heart

PS:您可以使用此链接 查看unicode字符转换

4. Object.getOwnPropertyDescriptors

此方法返回给定对象的所有属性的所有详细信息(包括 get``set 方法). 添加这个的主要动机是允许浅拷贝/克隆一个对象到另一个对象中,这个对象也拷贝getter和setter函数,而不是 Object.assign

Object.assign浅克隆除原始源对象的getter和setter函数以外的所有详细信息。

以下示例显示了Object.assignObject.getOwnPropertyDescriptorsObject.defineProperties之间的区别,以将原始对象 Car复制到新对象ElectricCarObject.getOwnPropertyDescriptors看到,discount  获取器和设置器函数也被复制到目标对象中。

之前…
//之前...var Car = {    name: 'BMW',    price: 1000000,    set discount(x) {        this.d = x;    },    get discount() {        return this.d;    },};//打印Car对象的“discount”属性的详细信息console.log(Object.getOwnPropertyDescriptor(Car, 'discount'));//打印结果..// {//     get: [Function: get],//     set: [Function: set],//     enumerable: true,//     configurable: true// }//使用Object.assign将Car的属性复制到ElectricCarconst ElectricCar = Object.assign({}, Car);//打印ElectricCar对象的“discount”属性的详细信息console.log(Object.getOwnPropertyDescriptor(ElectricCar, 'discount'));//打印// {//     value: undefined,//     writable: true,//     enumerable: true,//     configurable: true// }//⚠️ 请注意ElectricCar对象中的“discount”属性中缺少getter和setter!👎👎
以后…
//以后...var Car = {    name: 'BMW',    price: 1000000,    set discount(x) {        this.d = x;    },    get discount() {        return this.d;    },};//使用Object.defineProperties将Car的属性复制到ElectricCar2//并使用Object.getOwnPropertyDescriptors提取Car的属性const ElectricCar2 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(Car));//打印ElectricCar2对象“discount”属性的详细信息console.log(Object.getOwnPropertyDescriptor(ElectricCar2, 'discount'));//prints..// { get: [Function: get],  👈🏼👈🏼👈🏼//   set: [Function: set],  👈🏼👈🏼👈🏼//   enumerable: true,//   configurable: true // }// 请注意,getter和setter存在于ElectricCar2对象中,用于'discount'属性!
5. 在函数参数中添加尾随逗号(Add trailing commas in the function parameters)

这是一个小的更新,它允许我们在最后一个函数参数后面加上尾随逗号。为什么?为了帮助像git这样的工具,只会让新开发人员受到指责。

以下示例显示了问题和解决方案:

//问题//    #1 开发者创建function Person(    name,     age    ) {        this.name = name;        this.age = age;}//#2 开发者添加address function Person(    name,    age, // 添加尾随逗号     <--------因为逗号,开发人员会被git等工具所警告    address //添加新的参数  ) {        this.name = name;        this.age = age;        this.address = address; //添加的这行}//ES2017 解决//允许#1开发者添加尾随逗号//#1 创建如下function Person(    name,     ange,  //<------- 由于尾随逗号,#2开发者不需要改变这行    ) {        this.name = name;        this.age = age;}

注意:您也可以使用尾随逗号来调用函数

6. Async/Await

如果你问我,这是迄今为止最重要也是最有用的功能。异步函数(Async)允许我们不处理调用地狱,并使整个代码看起来很简单。

async关键字告诉JavaScript编译器以不同的方式处理函数,编译器在该函数内到达await关键字时暂停。它假设在await之后的表达式返回一个承诺,并等待承诺解决或拒绝后继续前进.

在下面的例子中,getAmount函数调用两个异步函数getUsergetBankBalance。我们可以承诺做到这一点,但使用 async await更优雅和简单

//不使用// ES2015 Promisefunction getAmount(userId) {    getUser(userId)        .then(getBankBalance)        .then(amount => {            console.log(amount);        });}//使用...// ES2017async function getAmount2(userId) {    var user = await getUser(userId);    var amount = await getBankBalance(user);    console.log(amount);}getAmount('1'); //$1,000getAmount2('1'); //$1,000function getUser(userId) {    return new Promise(resolve => {        setTimeout(() => {            resolve('john')        }, 1000)    });}function getBankBalance(user) {    return new Promise((resolve, reject) => {        setTimeout(() => {            if(user == 'john') {                resolve('$1,000');            } else {                reject(unknown user);            }        }, 1000)    });}
6.1 异步函数本身返回一个Promise(Async functions themselves return a Promise.)

如果您正在等待异步函数的结果,则需要使用Promise的then语法来捕获其结果。

在下面的例子中,我们想使用console.log记录结果,但不是在doubleAndAdd中。所以我们想等待并使用then语法将结果传递给console.log.

//异步函数本身返回一个Promiseasync function doubleAndAdd(a, b) {    a = await doubleAfterlSec(a);    b = await doubleAfterlSec(b);    return a + b;}//用法doubleAndAdd(1, 2).then(console.log);function doubleAfterlSec(param) {    return new Promise (resolve => {        setTimeout(resolve(param * 2), 1000);    });}     
6.2 并行调用异步/等待(Calling async/await in parallel)

在前面的例子中,我们调用等待两次,但每次我们等待一秒(总共2秒)。相反,我们可以让它并行,因为ab不依赖于使用Promise.all的其他对象。

//异步函数本身返回一个Promiseasync function doubleAndAdd(a, b) {    //注意:这边使用 Promise.all    //注意到使用数组解构,捕获结果    [a, b] = await Promise.all([doubleAfterlSec(a), doubleAfterlSec(b)]);    return a + b;}//用法doubleAndAdd(1, 2).then(console.log);function doubleAfterlSec(param) {    return new Promise (resolve => {        setTimeout(resolve(param * 2), 1000);    });}    
6.3 异步/等待函数时的错误处理(Error handling async/await functions

)

下面使用异步等待时有各种方法来处理错误。

Option 1 - 在函数中使用try catch

// 1. 使用 try catch async function doubleAndAdd(a, b) {    try {        a = await doubleAfterlSec(a);        b = await doubleAfterlSec(b);    } catch (e) {        return NaN; // return something    }    return a + b;}// 用法 doubleAndAdd('one', 2).then(console.log) //NaNdoubleAndAdd(1, 2).then(console.log) // 6function doubleAfterlSec(param) {    return new Promise((resolve, reject) => {        setTimeout(function() {            let val = param * 2;            isNaN(val) > reject(NaN) : resolve(val);        }, 1000);    });}

Option 2 — catch 获每个等待表达式(Catch every await expression)

由于每个await表达式都会返回Promise,因此您可以在每行上捕获错误,如下所示。

//Option 2 - *Catch* 每个等待线上的错误//因为每个等待表达本身就是一个Promiseasync function doubleAndAdd(a, b) {    a = await doubleAfter1Sec(a).catch(e => console.log('"a" is NaN')); // 👈  b = await doubleAfter1Sec(b).catch(e => console.log('"b" is NaN')); // 👈  if (!a || !b) {      return NaN;  }    return a + b;}//用法:doubleAndAdd('one', 2).then(console.log); // NaN  and logs:  "a" is NaNdoubleAndAdd(1, 2).then(console.log); // 6function doubleAfter1Sec(param) {    return new Promise((resolve, reject) => {        setTimeout(function() {            let val = param * 2;            isNaN(val) ? reject(NaN) : resolve(val);        }, 1000);    });}

Option 3 — 捕获整个异步等待函数(Catch the entire async-await function)

//Option 3 - 不要做任何事情,在function外捕获//因为异步/等待返回一个Promise,我们可以捕捉整个函数的错误async function doubleAndAdd(a, b) { a = await doubleAfter1Sec(a); b = await doubleAfter1Sec(b); return a + b;}//用法doubleAndAdd('one', 2).then(console.log).catch(console.log); // 👈👈🏼<------- use "catch"function doubleAfter1Sec(param) { return new Promise((resolve, reject) => {  setTimeout(function() {   let val = param * 2;   isNaN(val) ? reject(NaN) : resolve(val);  }, 1000); });}

ECMAScript 2018

ECMAScript目前处于最终草案中,将于2018年6月或7月发布。以下所有功能均在Stage-4中,并将成为ECMAScript 2018的一部分

1. 共享内存和原子 (Shared memory and atomics)

这是一个巨大的,非常先进的功能,是对JS引擎的核心增强。

主要思想是为JavaScript提供某种多线程功能,以便JS开发人员可以编写高性能,通过允许自己管理内存而不是让JS引擎管理内存来实现并发程序。

这是通过一种名为SharedArrayBuffer的新型全局对象来完成的,它基本上将数据存储在共享内存空间中.所以这个数据可以在主JS线程和网络工作者线程之间共享。

到现在为止,如果我们想要在主JS线程和Web工作者之间共享数据,我们必须复制数据并使用postMessage将其发送到其他线程。就这样子!

您只需使用SharedArrayBuffer,主线程和多个Web工作线程即可访问数据。

但在线程之间共享内存可能会导致竞争状况。为了避免竞争条件,引入了“Atomics”全局对象。当一个线程正在使用它的数据时,Atomics提供了各种方法来锁定共享内存。它还提供了安全地更新共享内存中的数据的方法。

建议通过某个库使用此功能,但现在没有构建在此功能之上的库。

如果您有兴趣,推荐阅读:

  1. From Workers to Shared Memory — lucasfcosta

  2. A cartoon intro to SharedArrayBuffers — Lin Clark

  3. Shared memory and atomics — Dr. Axel Rauschmayer

2. 标记模板字面限制已删除(模板字符串)

首先,我们需要澄清一下“Tagged Template literal”是什么,这样我们可以更好地理解这个特性。

在ES2015+中,有一项称为标记模板文字的功能,允许开发人员自定义如何插入字符串。例如,以标准方式插入字符串,如下所示

//标准字符串插值 const firstName = 'Raja';const greetings = `Hello ${firstName}!`;console.log(greetings); // "Hello Raja!"

在标记字面量中,可以编写一个函数来接收字符串文字的硬编码部分,例如['Hello','!']和替换变量,例如['Raja'],作为参数写入自定义函数(例如 greet),然后从该自定义函数中返回所需的任何内容。

下面的例子显示我们的自定义“Tag”函数greet“Good Morning!”,“Good afternoon,”等日子的时间,依此类推,取决于字符串文字的一天中的时间,并返回一个自定义字符串。

// "标记”函数返回一个自定义字符串文字.// 在这个例子中,greet调用timeGreet()来追加Good// Morning/Afternoon/Evening 视当天的时间而定.function greet(hardCodedPartsArray, ...replacementPartsArray) { console.log(hardCodedPartsArray); //[ 'Hello ', '!' ] console.log(replacementPartsArray); //[ 'Raja' ]let str = ''; hardCodedPartsArray.forEach((string, i) => {  if (i < replacementPartsArray.length) {   str += `${string} ${replacementPartsArray[i] || ''}`;  } else {   str += `${string} ${timeGreet()}`; //<-- 插入 Good morning/afternoon/evening here  } }); return str;}//用法const firstName = 'Raja';const greetings = greet`Hello ${firstName}!`; //👈🏼<-- 标记文字console.log(greetings); //'Hello  Raja! Good Morning!' 🔥function timeGreet() { const hr = new Date().getHours(); return hr < 12  ? 'Good Morning!'  : hr < 18 ? 'Good Afternoon!' : 'Good Evening!';}

现在我们已经讨论了“标记”功能是什么,许多人想要在不同的域中使用这个功能,比如在终端中使用命令和HTTP请求来编写URI等等。

⚠️ 标记字符串文字的问题

问题是ES2015和ES2016规范不允许使用“\u”(unicode),“\x”(十六进制)等转义字符,除非它们看起来完全像 \u00A9\uA9A\xA9

因此,如果您有一个内部使用其他域规则(如终端规则)的Tagged函数,那么可能需要使用\ubla123abla,它看起来不像\u0049\u{@F804},那么您将得到语法错误。

在ES2018中,只要标记函数返回具有“cooked”属性(其中无效字符为“undefined”)的对象中的值,然后将“raw”属性(与任何你想要的)。

function myTagFunc(str) {     return { "cooked": "undefined", "raw": str.raw[0] }} var str = myTagFunc `hi \ubla123abla`; //调用 myTagFuncstr // { cooked: "undefined", raw: "hi \\unicode" }   
3. 正则表达式的“dotall”标志

目前在正则中,虽然dot(“.”)应该匹配单个字符,但它不匹配像\n \r \f等新行字符。

比如:

//之前/first.second/.test('first\nsecond'); //false

此增强功能使点运算符可以匹配任何单个字符。为了确保这不会破坏任何内容,我们需要在创建正则时使用\s标志以使其正常工作。

//ECMAScript 2018/first.second/s.test('first\nsecond'); //true   Notice: /s 👈🏼

以下是提案文档中的整体API:

const re = /foo.bar/s;//或者 const re = new RegExp('foo.bar', 's');re.test('f00\nbar'); //truere.dotAll // truere.flags // 's'
4. RegExp命名组捕获(RegExp Named Group Captures 🔥)

这种增强功能为其他语言(如Python,Java等)提供了有用的RegExp功能,称为“命名组”。该功能允许开发人员编写RegExp以不同部分的格式(? …)提供名称(标识符)的RegExp中的组。然后,他们可以使用该名称轻松获取他们需要的任何群组。

4.1 基本命名组的例子

在下面的示例中,我们使用(?<year>) (?<month>) and (?year)名称对日期RegEx的不同部分进行分组。结果对象现在将包含具有属性yearmonthyear的具有相应值的groups属性。

//之前let re1 = /(\d{4})-(\d{2})-(\d{2})/;let result1 = re1.exec('2015-01-02');console.log(result1);////["2015-01-02", "2015", "01", "02", index: 0, input: "2015-01-02", groups: undefined]//以后(ES2018)let re2 = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;let result2 = re2.exec('2015-01-02');console.log(result2);//["2015-01-02", "2015", "01", "02", index: 0, input: "2015-01-02", groups: { year: '2015', month: '01', day: '02'}]//用法://你想要知道年份,你可以:console.log(result2.groups.year) //2015
4.2 在正则表达式本身内使用命名组

我们可以使用 \k<group name> 格式在正则表达式本身中反向引用该组。以下示例显示了它的工作原理。

//下面例子,有个组叫“fruit”//它既能匹配“apple”又能匹配“orange”.我们可以使用“\k<group name>”来回引用这个组的结果,(\k<fruit>)//所以它匹配==两侧的同一个单词let sameWords = /(?<fruit>apple|orange)==\k<fruit>/u;sameWords.test('apple==apple');  //truesameWords.test('orange==orange');  //truesameWords.test('apple==orange');  //false
4.3 在String.prototype.replace中使用命名组

现在,已命名的组功能被烘焙到String的replace实例方法中。所以我们可以很容易地交换字符串中的单词。

例如,更改“firstName, lastName” ==> “lastName, firstName”

// 更换“firstName, lastName” ==> “lastName, firstName”let re = /(?<firstName>[A-Za-z]+) (?<lastName>[A-Za-z]+$)/u;'Raja Rao'.replace(re, '$<lastName>, $<firstName>'); // "Rao, Raja"
5. 对象的其余属性(Rest properties for Objects)

rest运算符 ...(三个点)允许我们提取尚未提取的对象属性。

5.1 你可以使用rest来帮助只提取你想要的属性
// 提取 firstName 和 age// 将其余的项存储在“remaining”变量中。let { fristName, age, ...remaining } = {    fristName: 'john',    lastName: 'smith',    age: 20,    height: '5.10',    race: 'martian',};firstName; //johnage //20remaining //{ lastName: 'smith', height: '5.10', race: 'martian' }
5.2 更好的是,你可以删除不需要的!🔥🔥
//假设我们想要移除SSN,我们不需要遍历整个对象并创建一个新的cleanObj。// 简单地使用rest解构来提取出SSN,并将其余的保存在cleanObj中。let { SSN, ...cleanObj } = {    firstName: 'john',    lastName: 'smith',    SSN: '123-45-6789',    race: 'martian',}cleanObj; //{    firstName: 'john', lastName: 'smith', race: 'martian' }
6. 对象的扩展属性 (Spread properties for Objects)

扩展属性看起来就像具有三个点的Rest属性一样..., 但区别在于您使用扩展来创建(重新构建)新对象。

提示:扩展操作符用于等号的右侧。Rest用于等号的左侧

// person 和 account 合并const person = { fName: 'john', age: 20 };const account = { name: 'bofa', amount: '$1000' };// 通过扩展运算符提取person和account的属性,并将其添加到一个新对象中。const personAndAccount = { ...person, ...account };personAndAccount; // {fName: 'john', age: 20, name: 'bofa', amount: '$1000' }
7. RegExp 声明

这是对RegExp的增强,它允许我们确保某些字符串立即存在于其他字符串之前。

您现在可以使用一个组(?<=...)(问号,小于,等于)来查找肯定的声明。

此外,您可以使用(?<=...)(问号,小于,感叹号)来查看否定声明。从本质上讲,只要-ve声明通过,就会匹配。

正面声明:假设我们想要确保#符号在单词winning之前存在(即:#winning)并且希望正则表达式只返回字符串“winning”。这是你如何写它。

/(?<=#).*/.test('winning'); // false/(?<=#).*/.test('#winning'); // true//之前'#winning'.match(/#.*/)[0]; // '#wining',包含#//ES2018后'#winning'.match(/(?<=#).*/)[0]; // 'wining' 没有#, #是用来验证的。

否定声明:假设我们想从这些数字之前的具有€符号而不是$符号的行中提取数字。

// 3.00不能匹配,因为符号$在前面'A gallon of milk is $3.00'.match(/(?<\$)\d+\.?\d+/); // null// 2.43匹配成功,因为前面没有符号$//当匹配时,不包含符号€'A gallon of milk is €3.00'.match(/(?<\$)\d+\.?\d+/)[0]; //2.43
8. RegExp Unicode属性转义

要编写RegExp来匹配各种Unicode字符并不容易。像\w\W\d等只能匹配英文字符和数字。但是对于印度语,希腊语等其他语言的数字呢?

这就是Unicode Property Escapes的用武之地。结果是,Unicode为每个符号(字符)添加了元数据属性,并使用它来对各种符号进行分组或描述各种符号。

例如,Unicode数据库将所有印地语字符(हिन्दी)组合在一个名为具有值DevanagariScript脚本的属性下,另一个名为具有相同值 DevanagariScript_Extensions的属性 。所以我们可以搜索Script=Devanagari并获得所有印地文字符。

Devanagari(梵文)可用于马拉地语,北印度语,梵语等各种印度语言。

从ECMAScript 2018开始,我们可以使用\p转义字符和{Script=Devanagari}来匹配所有印度字符。 也就是说,我们可以在RegEx中使用:\p{Script=Devanagari}来匹配所有梵文字符。

//他跟随匹配多个印地文字符/^\p{Script=Devanagari}+$/u.test('हिन्दी'); //true  //PS:有3个印地文字符h

同样,Unicode数据库将所有希腊字符组合为Script_Extensions(和Script)属性,其值为 Greek。所以我们可以使用Script_Extensions=GreekScript=Greek来搜索所有希腊字符。

也就是说,我们可以在RegEx中使用:\p{Script = Greek}来匹配所有希腊字符。

//以下匹配一个希腊字符/\p{Script_Extensions=Greek}/u.test('π'); // true

此外,Unicode数据库将各种类型的Emojis(表情符号)存储在属性值为“true”的布尔属性EmojiEmoji_ComponentEmoji_PresentationEmoji_ModifierEmoji_Modifier_Base下。所以我们可以通过简单地选择表情符号来查找所有表情符号。

也就是说,我们可以使用:\p{Emoji}\Emoji_Modifier等来匹配各种Emojis。

下面的例子将会清楚。

// 下面是匹配表情字符/\p{Emoji}/u.test('❤️'); //true// 下面的操作失败了,因为黄色的emojis没有Emoji_Modifier!/\p{Emoji}\p{Emoji_Modifier}/u.test('✌️'); //false//下面的操作是匹配的,\p{Emoji} 紧跟后面是 \p{Emoji_Modifier}/\p{Emoji}\p{Emoji_Modifier}/u.test('✌🏽'); //true// 解释:// 默认情况下,胜利表情是黄色的。如果我们使用相同表情的棕色、黑色或其他,它们被认为是原始表情的变体,并使用两个unicode字符表示。一个是原始表情符号,另一个是颜色的unicode字符。在下面的例子中,虽然我们只看到一个棕色的胜利表情符号,但它实际上使用了两个unicode字符,一个用于表情符号,另一个用于棕色。//在Unicode数据库中,这些颜色具有Emoji_Modifier属性。因此,我们需要使用\p{Emoji}和\p{Emoji_Modifier}来正确和完全匹配棕色的表情符号。/\p{Emoji}\p{Emoji_Modifier}/u.test('✌🏽'); //true

最后,我们可以使用大写的“P”(\P)转义字符而不是小p(\p)来否定匹配。

参考:ECMAScript 2018 Proposal

9. Promise.prototype.finally()

finally() 是添加到Promise的新实例方法。他的主要想法是允许在解决或拒绝之后运行回调以帮助清理事情。finally回调被调用时没有任何价值,并且无论如何总是被执行。

我们来看看各种情况:

// Resolvelet started = true;let myPromise = newPromise(function(resolve, reject) {    resolve('all good');})    .then(val => {console.log(val); //all good    })    .catch(e => {console.log(e)l // 跳过    })    .finally(() => {console.log('This function is always executed!');        started = false; // 清除    })// Rejectlet started = true;let myPromise = newPromise(function(resolve, reject) {    resolve('reject apple');})    .then(val => {console.log(val); //reject apple    })    .catch(e => {console.log(e)l //catch被跳过    })    .finally(() => {//注意这里没有任何值console.log('This function is always executed!');        started = false; //清除    })//错误情况1//Promise 抛出错误let started = true;let myPromise = newPromise(function(resolve, reject) {thrownewError('Error');})    .then(val => {console.log(val); // 跳过    })    .catch(e => {console.log(e)l // 出现错误catch被调用    })    .finally(() => {// 注意这里没有任何值console.log('This function is always executed!');        started = false; // 清除    })// 错误从“catch”事件中抛出let started = true;let myPromise = newPromise(function(resolve, reject) {thrownewError('something happend');})    .then(val => {console.log(val); // 跳过    })    .catch(e => {thrownewError('throw another error')    })    .finally(() => {// 注意这里没有任何值console.log('This function is always executed!');        started = false; //清除// 错误形式的捕获将需要在其他地方调用函数来处理    })
10. 异步迭代 (Asynchronous Iteration)

这是一个非常有用的功能。基本上它允许我们轻松创建异步代码循环!

这个特性增加了一个新的“for-await-of”循环,允许我们调用异步函数来返回Promise(或带有一系列Promise的数组)。很酷的是循环等待每个Promise在下一个循环前解析。

const promises = [newPromise(resolve => resolve(1)),newPromise(resolve => resolve(2)),newPromise(resolve => resolve(3)),]// 之前// for-of使用常规同步迭代器// 不等待Promise 去解决asyncfunctiontest1() {for (const obj of promise) {console.log(obj) // 记录3 条Promise对象    }}// 以后// for-wait-of使用异步迭代asyncfunctiontest2() {forawait (const obj of promises) {console.log(obj); // 1,2, 3    }}test1() // promise, promise, promisetest2() // 1,2,3

最后,为你推荐

【第1243期】一次掌握 JavaScript ES5 到 ES8 数组内容

关于本文译者:@hypers译文:http://blog.hypers.io/2018/04/11/es2016-17-18/作者:@rajaraodv 原文:https://medium.freecodecamp.org/here-are-examples-of-everything-new-in-ecmascript-2016-2017-and-2018-d52fa3b5a70e

【招聘】YMFE – 去哪儿网移动架构组招聘前端、安卓、iOS