首页
Preview

以下是 ECMAScript 2016、2017 和 2018 新增内容的示例:

在JavaScript(ECMAScript)中,新的功能层出不穷,很难跟上步伐,更难找到有用的代码示例。因此,在本文中,我将涵盖所有列在TC39完成提案中的18个功能,这些功能都是在ES2016、ES2017和ES2018(最终草案)中添加的,并使用有用的示例展示它们。

这是一篇相当长的文章,但应该很容易阅读。把它当作“Netflix的连续阅读”。通过本文,我保证你会对所有这些功能有很多了解。

好的,让我们逐一讨论这些功能。

  1. Array.prototype.includes

includes是Array的一个简单实例方法,帮助我们轻松地查找一个项是否在数组中(与indexOf不同,它包括NaN)。

Trivia:JavaScript规范的人们想把它命名为contains,但显然Mootools已经使用了这个名字,所以他们使用了includes

  1. Exponentiation中缀运算符

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

  1. Object.values()

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

  1. Object.entries()

Object.entries()Object.keys()相关,但它不仅返回键,还以数组方式返回键和值。这使得在循环中使用对象或将对象转换为Map非常简单。

  1. 字符串填充

String添加了两个实例方法——String.prototype.padStartString.prototype.padEnd——允许将空字符串或其他字符串附加/预先附加到原始字符串的开头或结尾。

这在我们想要对齐事物的情况下非常方便,例如在漂亮的打印显示或终端打印中。

padStart示例:

在下面的示例中,我们有一个数字列表,长度不同。我们想要在显示目的上在所有项目之前加上“0”,以便所有项目都具有相同的10位数字长度。我们可以使用padStart(10,'0')轻松实现这一点。

padEnd示例:

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

下面的示例是一个很好的实际示例,展示了padEndpadStartObject.entries如何结合起来产生美丽的输出。

⚠️在表情符号和其他双字节字符上使用padStart和padEnd

表情符号和其他双字节字符使用多个字节的Unicode表示。因此,padStart和padEnd可能无法按预期工作!⚠️

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

这是因为❤️有2个代码点长('\u2764\uFE0F')!单词“heart”本身有5个字符,因此我们只剩下5个字符要填充。所以发生的事情是JS使用'\u2764\uFE0F'填充了两个心形符号,并产生了❤️❤️。对于最后一个字符,它只使用心形符号的第一个字节\u2764,这会产生❤。

所以我们最终得到:❤️❤️❤heart

PS:你可以使用此链接查看Unicode字符转换。

4. Object.getOwnPropertyDescriptors

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

Object.assign浅复制所有细节,但不复制原始源对象的getter和setter函数。

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

之前...

之前 — 使用Object.assign

之后...

ECMAScript 2017(ES8)— Object.getOwnPropertyDescriptors

var Car = {
 name: 'BMW',
 price: 1000000,
 set discount(x) {
  this.d = x;
 },
 get discount() {
  return this.d;
 },
};//Print details of Car object's 'discount' property
console.log(Object.getOwnPropertyDescriptor(Car, 'discount'));
//prints..
// { 
//   get: [Function: get],
//   set: [Function: set],
//   enumerable: true,
//   configurable: true
// }//Copy Car's properties to ElectricCar using Object.assign
const ElectricCar = Object.assign({}, Car);//Print details of ElectricCar object's 'discount' property
console.log(Object.getOwnPropertyDescriptor(ElectricCar, 'discount'));
//prints..
// { 
//   value: undefined,
//   writable: true,
//   enumerable: true,
//   configurable: true 
  
// }
//⚠️Notice that getters and setters are missing in ElectricCar object for 'discount' property !👎👎//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 
// }
// Notice that getters and setters are present in the ElectricCar2 object for 'discount' property!

5. 在函数参数中添加尾逗号

这是一个次要的更新,允许我们在最后一个函数参数后添加尾逗号。为什么?为了帮助像git blame这样的工具,以确保只有新开发人员受到指责。

下面的示例显示了问题和解决方案。

ECMAScript 2017(ES 8)—函数参数中的尾逗号

注意:你也可以使用尾逗号调用函数!

6. Async/Await

到目前为止,如果你问我,这是最重要和最有用的功能。异步函数使我们不必处理回调地狱,并使整个代码看起来简单。

async关键字告诉JavaScript编译器以不同的方式处理该函数。每当编译器在该函数中达到await关键字时,它就会暂停。它假定await之后的表达式返回一个Promise,并等待Promise被解析或拒绝,然后再继续。

在下面的示例中,getAmount函数调用两个异步函数getUsergetBankBalance。我们可以使用Promise来实现,但是使用async await更加优雅和简单。

ECMAScript 2017(ES 8)—异步等待基本示例

6.1 Async函数本身返回Promise。

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

在以下示例中,我们想使用console.log记录结果,但不在doubleAndAdd中。因此,我们要等待并使用then语法将结果传递给console.log

ECMAScript 2017(ES 8)—异步等待本身返回Promise

6.2 并行调用async/await

在上一个示例中,我们调用了两次await,但是每次我们都等待一秒钟(总共2秒钟)。相反,我们可以并行化,因为ab不相互依赖,使用Promise.all

ECMAScript 2017(ES 8)—使用Promise.all并行化async/await

6.3 错误处理async/await函数

使用async await处理错误的方法有很多种。

选项1 — 在函数内部使用try catch

ECMAScript 2017 — 在async/await函数内部使用try catch

//Option 1 - Use try catch within the function
async function doubleAndAdd(a, b) {
 try {
  a = await doubleAfter1Sec(a);
  b = await doubleAfter1Sec(b);
 } catch (e) {
  return NaN; //return something
 }return a + b;
}
//🚀Usage:
doubleAndAdd('one', 2).then(console.log); // NaN
doubleAndAdd(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);
 });
}

选项2 — 捕获每个await表达式

由于每个await表达式都返回一个Promise,因此你可以像下面显示的那样在每行上捕获错误。

ECMAScript 2017 — 在每个await表达式上使用try catch

//Option 2 - *Catch* errors on  every await line
//as each await expression is a Promise in itself
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;
}//🚀Usage:
doubleAndAdd('one', 2).then(console.log); // NaN  and logs:  "a" is NaN
doubleAndAdd(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);
 });
}

选项3 — 在最后捕获整个async/await函数

ECMAScript 2017 — 在最后捕获整个async/await函数

//Option 3 - Dont do anything but handle outside the function
//since async / await returns a promise, we can catch the whole function's error
async function doubleAndAdd(a, b) {
 a = await doubleAfter1Sec(a);
 b = await doubleAfter1Sec(b);
 return a + b;
}//🚀Usage:
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年6月或7月发布。下面介绍的所有功能都处于Stage-4,并将成为ECMAScript 2018的一部分。

1. 共享内存和原子操作```

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

主要思想是将一些多线程功能引入JavaScript,以便JS开发人员可以编写高性能的并发程序,通过允许自己管理内存来管理内存,而不是让JS引擎来管理内存。

这是通过一种名为SharedArrayBuffer的全局对象来实现的,该对象基本上将数据存储在一个共享 内存空间中。因此,这些数据可以在主JS线程和Web Worker线程之间共享。

到目前为止,如果我们想在主JS线程和Web Worker之间共享数据,我们必须复制数据并使用postMessage将其发送到其他线程。现在不用了!

你只需使用SharedArrayBuffer,数据即可立即被主线程和多个Web Worker线程访问。

但在线程之间共享内存可能会导致竞态条件。为了避免竞态条件,引入了“Atomics”全局对象。_Atomics_提供了各种方法,在线程使用其数据时锁定共享内存。它还提供了安全更新共享内存中这些数据的方法。

建议通过某个库来使用此功能,但目前还没有建立在此功能之上的库。

如果你感兴趣,我建议阅读:

Lin Clark

2. 消除标记模板文字限制

首先,我们需要澄清什么是“标记模板文字”,以便更好地理解此功能。

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

在标记文字中,你可以编写一个函数来接收字符串文字的硬编码部分,例如 [ ‘Hello ‘, ‘!’ ],以及替换变量,例如 [ &#39;Raja&#39;],作为自定义函数(例如 greet)的参数,并从该自定义函数返回任何你想要的内容。

下面的示例显示了我们的自定义“标记”函数greet将一天中的时间(如“早上好!”,“下午好!”等)附加到字符串文字中,并返回自定义字符串。

标记函数示例,显示自定义字符串插值

//A "Tag" function returns a custom string literal.
//In this example, greet calls timeGreet() to append Good //Morning/Afternoon/Evening depending on the time of the day.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()}`; //<-- append Good morning/afternoon/evening here
  }
 });
 return str;
}//🚀Usage:
const firstName = 'Raja';
const greetings = greet`Hello ${firstName}!`; //👈🏼<-- Tagged literalconsole.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\u{2F804}\xA9

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

在ES2018中,规则被放松,允许使用这样看似无效的转义字符,只要标记函数将值返回为带有“cooked”属性的对象(其中无效字符为“undefined”),然后是一个“raw”属性(包含任何你想要的内容)。

function myTagFunc(str) { 
 return { "cooked": "undefined", "raw": str.raw[0] }
} 

var str = myTagFunc `hi \ubla123abla`; //call myTagFunc

str // { cooked: "undefined", raw: "hi \\unicode" }

3. 正则表达式的“dotall”标志

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

例如:

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

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

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

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

ECMAScript 2018 — Regex dotAll功能允许通过/s标志匹配任何字符,包括\n

4. RegExp命名组捕获 🔥

这个增强功能将其他语言(如Python、Java等)中称为“命名组”的有用的RegExp功能引入了JavaScript。此功能允许编写RegExp的开发人员为RegExp中不同部分的组提供格式为(?&lt;name&gt;...)的名称(标识符)。然后,他们可以使用该名称轻松获取他们需要的任何组。## 4.1 命名分组基本示例

在下面的示例中,我们使用 (?<year>), (?<month>)(?<day>) 名称来分组日期正则表达式的不同部分。生成的对象现在将包含一个 groups 属性,其中包含相应值的 yearmonthday 属性。

ECMAScript 2018 — 正则表达式命名分组示例

4.2 在正则表达式中使用命名分组

我们可以使用 \k<group name> 格式在正则表达式本身内部回溯分组。下面的示例展示了它的工作原理。

ECMAScript 2018 — 正则表达式命名分组通过 \k 回溯

4.3 在 String.prototype.replace 中使用命名分组

现在,命名分组功能已经内置到了 String 的 replace 实例方法中。因此,我们可以轻松地交换字符串中的单词。

例如,将“firstName,lastName”更改为“lastName,firstName”。

ECMAScript 2018 — 在 replace 函数中使用正则表达式的命名分组功能

5. 对象的剩余属性

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

5.1 你可以使用剩余属性来帮助提取你想要的属性

ECMAScript 2018 — 剩余运算符解构对象

5.2 更好的是,你可以删除不想要的项!🔥🔥

ECMAScript 2018 — 剩余运算符解构对象

6. 对象的扩展属性

扩展属性看起来与剩余属性完全相同,都是三个点 ...,但区别在于你使用扩展来创建(重构)新对象。

提示:扩展运算符用于等号的右侧。而剩余运算符用于等号的左侧。

ECMAScript 2018 — 扩展解构对象

7. RegExp 向后断言

这是 RegEx 的一个增强功能,允许我们确保某些字符串在另一个字符串之前立即存在。

现在,你可以使用组 (?<=...)(问号,小于,等于)来查找正面断言。

此外,你可以使用 (?<!...)(问号,小于,感叹号)来查找负面断言。本质上,只要负面断言通过,就会匹配。

正面断言:假设我们想确保单词“winning”之前存在 # 符号(即 #winning),并且希望正则表达式仅返回字符串“winning”。下面是你的编写方式。

ECMAScript 2018 — (?<=...) 用于正面断言

负面断言:假设我们想从具有 € 符号但在数字之前没有 $ 符号的行中提取数字。

ECMAScript 2018 — (?<!...) 用于负面断言

8. RegExp Unicode Property Escapes

编写用于匹配各种 Unicode 字符的 RegEx 并不容易。例如,像 \w\W\d 等只匹配英文字符和数字。但是其他语言(如印地语、希腊语等)中的数字呢?

这就是 Unicode 属性转义的作用。Unicode 为每个符号(字符)添加元数据属性,并使用它来分组或描述各种符号。

例如,Unicode 数据库将所有印地语字符(हिन्दी)分组到一个名为 Script 的属性下,其值为 Devanagari,并将其分组到另一个名为 Script_Extensions 的属性下,其值也为 Devanagari。因此,我们可以搜索 Script=Devanagari 并获取所有印地语字符。

从 ECMAScript 2018 开始,我们可以使用 \p 转义字符,加上 {Script=Devanagari} 来匹配所有这些印地语字符。也就是说,我们可以在正则表达式中使用:\p{Script=Devanagari} 来匹配所有 Devanagari 字符。

ECMAScript 2018 — 显示 \p

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

同样地,Unicode 数据库将所有希腊字符都分组到了 Script_Extensions (和 Script )属性中,属性值为 Greek。因此,我们可以使用 Script_Extensions=GreekScript=Greek 来搜索所有希腊字符。

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

ECMAScript 2018 — 显示 \p

//The following matches a single Greek character
/\p{Script_Extensions=Greek}/u.test('π'); // true

此外,Unicode 数据库将各种类型的表情存储在布尔属性 EmojiEmoji_ComponentEmoji_PresentationEmoji_ModifierEmoji_Modifier_Base 中,属性值为 true。因此,我们可以通过选择 Emoji 为 true 来搜索所有表情。

也就是说,我们可以使用:\p{Emoji}\Emoji_Modifier** 等来匹配各种类型的表情。**

下面的示例将说明一切。

ECMAScript 2018 — 显示如何使用 \p 匹配各种表情

//The following matches an Emoji character
/\p{Emoji}/u.test('❤️'); //true//The following fails because yellow emojis don't need/have Emoji_Modifier!
/\p{Emoji}\p{Emoji_Modifier}/u.test('✌️'); //false//The following matches an emoji character\p{Emoji} followed by \p{Emoji_Modifier}
/\p{Emoji}\p{Emoji_Modifier}/u.test('✌🏽'); //true//Explaination:
//By default the victory emoji is yellow color.
//If we use a brown, black or other variations of the same emoji, they are considered
//as variations of the original Emoji and are represented using two unicode characters.
//One for the original emoji, followed by another unicode character for the color.
//
//So in the below example, although we only see a single brown victory emoji,
//it actually uses two unicode characters, one for the emoji and another
// for the brown color.
//
//In Unicode database, these colors have Emoji_Modifier property.
//So we need to use both \p{Emoji} and \p{Emoji_Modifier} to properly and
//completely match the brown emoji.
/\p{Emoji}\p{Emoji_Modifier}/u.test('✌🏽'); //true

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

参考:

8. Promise.prototype.finally()

finally() 是添加到 Promise 中的新实例方法。其主要思想是允许在 resolvereject 之后运行回调以帮助清理事务。finally 回调被调用时不带任何值,并且无论如何都会执行。

让我们看看各种情况。

ECMAScript 2018 — 在 resolve 情况下的 finally()

ECMAScript 2018 — 在 reject 情况下的 finally()

ECMASCript 2018 — 在 Promise 中抛出错误的 finally()

ECMAScript 2018 — 在 catch 中抛出错误的 finally()

9. 异步迭代

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

此功能添加了一个新的 “for-await-of” 循环,允许我们在循环中调用返回 Promise(或一堆 Promise 的数组)的异步函数。酷的是,该循环在进行下一个循环之前等待每个 Promise 解析。

ECMAScript 2018 — 通过 for-await-of 实现异步迭代

译自:https://rajaraodv.medium.com/here-are-examples-of-everything-new-in-ecmascript-2016-2017-and-2018-d52fa3b5a70e

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
一个人玩
先找到想要的,然后出发

评论(0)

添加评论