1. 首页

汇总ECMAScript 2016、2017、2018中所有新特性

跟踪JavaScript (ECMAScript)中的新内容是很困难的,而且更难找到有用的代码示例。

因此,在本文中将介绍 TC39(最终草案) 在ES2016、ES2017和ES2018中添加的已完成提案中列出的所有18个特性,并给出有用的示例。

JS中文网 - 前端进阶资源分享

1.Array.prototype.includes

include 是数组上的一个简单实例方法,可以轻松查找数组中是否有指定内容(包括 NaN)。

JS中文网 - 前端进阶资源分享

2.求幂操作符

像加法和减法这样的数学运算分别有像 + 和 – 这样运算符。与它们类似,** 运算符通常用于指数运算。在ECMAScript 2016中,引入了 ** 代替 Math.pow。

JS中文网 - 前端进阶资源分享

JS中文网 - 前端进阶资源分享

1.Object.values()

Object.values()是一个类似于Object.keys()的新函数,但返回对象自身属性的所有值,不包括原型链中的任何值。

JS中文网 - 前端进阶资源分享

2.Object.entries()

Object.entries()与Object.keys 类似,但它不是仅返回键,而是以数组方式返回键和值。 这使得在循环中使用对象或将对象转换为映射等操作变得非常简单。

例一:

JS中文网 - 前端进阶资源分享

例二:

JS中文网 - 前端进阶资源分享

3.字符串填充

在String.prototype中添加了两个实例方法:String.prototype.padStart 和 String.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’)轻松实现这一点。

JS中文网 - 前端进阶资源分享

3.2 padEnd 例子:

当我们打印多个不同长度的项目并想要右对齐它们时,padEnd非常有用。

下面的示例是关于padEnd、padStart和 Object.entries 的一个很好的实际示例:

JS中文网 - 前端进阶资源分享

const cars = {
  '🚙BMW': '10',
  '🚘Tesla': '5',
  '🚖Lamborghini': '0'
}

Object.entries(cars).map(([name, count]) => {
  console.log(`${name.padEnd(20, ' -')}  Count: ${count.padStart(3, '0')}`)
})
// 打印
// 🚙BMW - - - - - - -  Count: 010
// 🚘Tesla - - - - - -  Count: 005
// 🚖Lamborghini - - -  Count: 000



3.3 ⚠️ 注意padStart和padEnd 在Emojis和其他双字节字符上的使用

Emojis和其他双字节字符使用多个unicode字节表示。所以padStart padEnd可能不会像预期的那样工作!⚠️

例如:假设我们要垫达到10个字符的字符串的心❤️emoji。结果如下:


'heart'.padStart(10, "❤️"); // prints.. '❤️❤️❤heart'

这是因为 ❤️ 长2个字节(’ u2764 uFE0F’)! 单词 heart 是5个字符,所以我们只剩下5个字符来填充。 所以 JS 使用 (‘u2764uFE0F’ ) 填充两颗心并生成 ❤️❤️。 对于最后一个,它只使用 (‘u2764uFE0F’ ) 的第一个字节(u2764)来生成,所以是 ❤;

4.Object.getOwnPropertyDescriptors

此方法返回给定对象的所有属性的所有属性(包括getter setter set方法),添加这个的主要目的是允许浅 拷贝/克隆到另一个对象中的对象,类似 bject.assign。

Object.assign 浅拷贝除原始对象的 getter 和 setter 方法之外的所有属性。

下面的示例显示了 Object.assign 和 Object.getOwnPropertyDescriptors 以及Object.defineProperties 之间的区别,以将原始对象 Car 复制到新对象 ElectricCar 中。 可以看到使用 Object.getOwnPropertyDescriptors,discount 的 getter 和 setter 函数也被复制到目标对象中。

JS中文网 - 前端进阶资源分享

使用 Object.defineProperties

JS中文网 - 前端进阶资源分享


var Car = { name: 'BMW', price: 1000000, set discount(x) { this.d = x; }, get discount() { return this.d; }, }; console.log(Object.getOwnPropertyDescriptor(Car, 'discount')); // 打印 // { // get: [Function: get], // set: [Function: set], // enumerable: true, // configurable: true // } // 使用 Object.assign 拷贝对象 const ElectricCar = Object.assign({}, Car); //Print details of ElectricCar object's 'discount' property console.log(Object.getOwnPropertyDescriptor(ElectricCar, 'discount')); // 打印 // { // value: undefined, // writable: true, // enumerable: true, // configurable: true // } // //⚠️请注意,“discount” 属性的 ElectricCar 对象中缺少getter和setter!👎👎 //Copy Car's properties to ElectricCar2 using Object.defineProperties //and extract Car's properties using Object.getOwnPropertyDescriptors const ElectricCar2 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(Car)); //Print details of ElectricCar2 object's 'discount' property console.log(Object.getOwnPropertyDescriptor(ElectricCar2, 'discount')); //prints.. // { get: [Function: get], 👈🏼👈🏼👈🏼 // set: [Function: set], 👈🏼👈🏼👈🏼 // enumerable: true, // configurable: true // } // 请注意,在ElectricCar2对象中存在“discount”属性的getter和setter !

5.函数参数的尾逗号

ES2017允许函数的最后一个参数有尾逗号(trailing comma), 此前,函数定义和调用时,都不允许最后一个参数后面出现逗号。这一变化将鼓励开发人员停止丑陋的“行以逗号开头”的习惯。这对于版本管理系统来说,就会显示添加逗号的那一行也发生了变动。

JS中文网 - 前端进阶资源分享

6.Async/Await

到目前为止,个人感受是这是最重要和最有用的功能。 async 函数允许我们不处理回调地狱,并使整个代码看起来很简单。

async 关键字告诉 JavaScript 编译器以不同的方式对待函数。每当编译器到达函数中的 await 关键字时,它就会暂停。它假定 wait 之后的表达式返回一个 promise ,并在进一步移动之前等待该 promise 被 resolved 或 rejected。

在下面的示例中,getAmount 函数调用两个异步函数getUser和getBankBalance。使用 async await更加优雅和简单达到有有序的调用 getUser 与 getBankBalance。

JS中文网 - 前端进阶资源分享

6.1.async 函数默认返回一个 promise

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

在以下示例中,我们希望使用 console.log 来打印结果但是不在 doubleAndAdd 函数里面操作。 因为 async 返回是一个 promise 对象,所以可以在 then 里面执行我们一些打印操作。

JS中文网 - 前端进阶资源分享

6.2 并行调用 async/await

在前面的例子中,我们调用doubleAfterlSec ,但每次我们等待一秒钟(总共2秒)。 相反,我们可以使用 Promise.all 将它并行化为一个并且互不依赖于。

JS中文网 - 前端进阶资源分享

6.3 async/await 函数对错误的处理

在使用async/wait时,有多种方法可以处理错误。

方法一:在函数内使用 try catch

JS中文网 - 前端进阶资源分享


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

方法二:在 await 后使用 catch 捕获错误

JS中文网 - 前端进阶资源分享


// 方法二:在 await 后使用 catch 获取错误 async 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 NaN doubleAndAdd(1, 2).then(console.log); // 6 function doubleAfter1Sec(param) { return new Promise((resolve, reject) => { setTimeout(function() { let val = param * 2; isNaN(val) ? reject(NaN) : resolve(val); }, 1000); }); }

方法三:在整个的 async-await 函数捕获错误

JS中文网 - 前端进阶资源分享


//方法三:在整个的 async-await 函数捕获错误 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); }); }

JS中文网 - 前端进阶资源分享

7.共享内存 和 Atomics

这是一个巨大的、相当高级的特性,是JS引擎的核心增强。

其主要原理是在 JavaScript 中引入某种多线程特性,以便JS开发人员将来可以通过允许自己管理内存而不是让 JS 引擎管理内存来编写高性能的并发程序。

这是通过一种名为 SharedArrayBuffer (即 共享数组缓冲区) 的新类型的全局对象实现的,该对象本质上是将数据存储在共享内存空间中。因此,这些数据可以在主JS线程和web工作线程之间共享。

到目前为止,如果我们想在主 JS 线程和 web 工作者之间共享数据,我们必须复制数据并使用postMessage 将其发送到另一个线程。

你只需使用SharedArrayBuffer,数据就可以立即被主线程和多个web工作线程访问。workers 之间的协调变得更简单和更快(与 postMessage() 相比)。

但是在线程之间共享内存会导致竞争条件。为了帮助避免竞争条件,引入了 “Atomics” 全局对象。 Atomics 提供了各种方法来在线程使用其数据时锁定共享内存。 它还提供了安全地更新该共享内存中的搜索数据的方法。

如果你这对个感兴趣,可以阅读以下文章:

8. Tagged Template literal restriction removed

首先,我们需要知道的什么是 Template literals(“标记的模板文字”),以便更好地理解这个特性。Template literals是一个ES2015特性,它使用反引号包含一个字符串字面量,并且支持嵌入表达式和换行,如:

JS中文网 - 前端进阶资源分享

下面的例子显示,我们的自定义“Tag” 函数 greet 添加了一天中的时间,比如“Good Morning!” “Good afternoon” 等等,取决于一天中的时间字符串的文字和返回自定义字符串。

JS中文网 - 前端进阶资源分享


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}!`; //👈🏼<-- Tagged literal 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!'; }

现在我们讨论了什么是“标记”函数,许多人希望在不同的领域中使用这个特性,比如在Terminal中用于命令,在组成 uri 的 HTTP 请求中,等等。

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

问题是ES2015和ES2016规范不允许使用像“u”(unicode)、“x”(十六进制)这样的转义字符,除非它们看起来完全像“ u00A9”或u{2F804}或xA9。

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


function myTagFunc(str) { return { "cooked": "undefined", "raw": str.raw[0] } } var str = myTagFunc `hi \ubla123abla`; //call myTagFunc str // { cooked: "undefined", raw: "hi \\unicode" }

9.用于正则表达式的“dotall”标志

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

例如:


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

这种增强使 点 运算符能够匹配任何单个字符。为了确保它不会破坏任何东西,我们需要在创建正则表达式时使用s标志。


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

更多的方法,请看这里

JS中文网 - 前端进阶资源分享

10.RegExp Named Group Captures

这种增强 RegExp特性借鉴于像Python、Java等其他语言,因此称为“命名组”。这个特性允许编写开发人员以(…)格式为 RegExp 中的组的不同部分提供名称(标识符),使用可以用这个名称轻松地获取他们需要的任何组。

10.1 Named group 的基础用法

在下面的示例中,我们使用 (?) (?) 和 (?) 名称对日期正则表达式的不同部分进行分组。结果对象现在将包含一个groups属性,该属性具有 year、month和 day 的相应值。

JS中文网 - 前端进阶资源分享


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' ] // ECMAScript 2018 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

10.2 在 regex 内使用 Named groups

使用\k<组名>格式来反向引用正则表达式本身中的组,例如:

JS中文网 - 前端进阶资源分享


// 在下面的例子中,我们有一个包合的“水果”组。 // 它既可以配“苹果”,也可以配“橘子”, // 我们可以使用 “\k<group name>” (\k<fruit>) 来反向引用这个组的结果, // 所以它可以匹配“=”相同的单词 let sameWords = /(?<fruit>apple|orange)=\k<fruit>/u; sameWords.test('apple=apple') // true sameWords.test('orange=orange') // true sameWords.test('apple=orange') // false

10.3 在 String.prototype.replace 中使用 named groups

在 String.prototype.replace 方法中使用 named groups。所以我们能更快捷的交换词。

例如,把 “firstName, lastName” 改成 “lastName, firstName”。

JS中文网 - 前端进阶资源分享

let re = /(?<firstName>[A-Za-z]+) (?<lastName>[A-Za-z]+$)/u;

'Hello World'.replace(re, `$<lastName>, $<firstName>`) // "World, Hello"


11.对象的 Rest 属性

rest操作 …(三个点)允许挑练我们需要的属性。

11.1 通过 Rest 解构你需要的属性

JS中文网 - 前端进阶资源分享


let { firstName, age, ...remaining } = { firstName: '王', lastName: '智艺', age: 27, height: '1.78', race: '黄' } firstName; // 王 age; // 27 remaining; // { lastName: "智艺", height: "1.78", race: "黄" }

12.对象的扩展属性

扩展 和 解析 的 三个点是一样的,但是不同的是你可以用 扩展 去新建或者组合一个新对象。

扩展 是对齐赋值的右运算符, 而 解构 是左运算符。

JS中文网 - 前端进阶资源分享


const person = { fName: '小明', age: 20 }; const account = { name: '小智', amount: '$1000'}; const personAndAccount = { ...person, ...account }; personAndAccount; // {fName: "小明", age: 20, name: "JS中文网 前端进阶", amount: "$1000"}

13.正则表达式反向(lookbehind)断言

断言(Assertion)是一个对当前匹配位置之前或之后的字符的测试, 它不会实际消耗任何字符,所以断言也被称为“非消耗性匹配”或“非获取匹配”。

正则表达式的断言一共有 4 种形式:

  • (?=pattern) 零宽正向肯定断言(zero-width positive lookahead assertion)
  • (?!pattern) 零宽正向否定断言(zero-width negative lookahead assertion)
  • (?<=pattern) 零宽反向肯定断言(zero-width positive lookbehind assertion)
  • (?<!pattern) 零宽反向否定断言(zero-width negative lookbehind assertion)

你可以使用组(?<=…) 去正向断言,也可以用 (?<!…) 去取反。

正向断言: 我们想确保 # 在 winning 之前。(就是#winning),想正则匹配返回 winning。下面是写法:

JS中文网 - 前端进阶资源分享

反向断言:匹配一个数字,有 € 字符而没有 $ 字符在前面的数字。

JS中文网 - 前端进阶资源分享

更多内容可以参考:S2018 新特征之:正则表达式反向(lookbehind)断言

14. RegExp Unicode Property Escapes

用正则去匹配 Unicode 字符是很不容易的。像 w , W , d 这种只能匹配英文字符和数字。但是其他语言的字符怎么办呢,比如印度语,希腊语?

例如 Unicode 数据库组里把所有的印度语字符,标识为 Script = Devanagari。还有一个属性 Script_Extensions, 值也为 Devanagari。 所以我们可以通过搜索 Script=Devanagari,得到所有的印度语。

Devanagari 可以用于印度的各种语言,如Marathi, Hindi, Sanskrit。

在 ECMAScript 2018 里, 我们可以使用 p 和 {Script=Devanagari} 匹配那些所有的印度语字符。也就是说 p{Script=Devanagari} 这样就可以匹配。

JS中文网 - 前端进阶资源分享


//The following matches multiple hindi character /^\p{Script=Devanagari}+$/u.test('हिन्दी'); //true //PS:there are 3 hindi characters h

同理,希腊语的语言是 Script_Extensions 和 Script 的值等于 Greek 。也就是用Script_Extensions=Greek or Script=Greek 这样就可以匹配所有的希腊语,也就是说,我们用 p{Script=Greek} 匹配所有的希腊语。

进一步说,Unicode 表情库里存了各式各样的布尔值,像 Emoji, Emoji_Component, Emoji_Presentation, Emoji_Modifier, and Emoji_Modifier_Base 的值,都等于 true。所以我们想搜 Emoji 等于 ture,就能搜到所有的表情。

我们用 \p{Emoji} ,\Emoji_Modifier 匹配所有的表情。

JS中文网 - 前端进阶资源分享

参考文献:

  1. ECMAScript 2018 Proposal
  2. https://mathiasbynens.be/note…

15. Promise.prototype.finally()

finally() 是 Promise 新增的一个实例方法。意图是允许在 resolve/reject 之后执行回调。finally 没有返回值,始终会被执行。

让我们看看各种情况。

JS中文网 - 前端进阶资源分享

JS中文网 - 前端进阶资源分享

JS中文网 - 前端进阶资源分享

JS中文网 - 前端进阶资源分享

16.异步迭代(Asynchronous Iteration)

这是一个极其好用的新特性。让我们能够非常容易的创建异步循环代码。

JS中文网 - 前端进阶资源分享

作者:前端小智
链接:https://medium.freecodecamp.org/

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:汇总ECMAScript 2016、2017、2018中所有新特性

链接:https://www.javascriptc.com/3502.html

« javascript代码规范,应该这样写
Vue 中的无状态组件你搞懂了没»
Flutter 中文教程资源

相关推荐

QR code