从ES5到ESNext-这是自2015年以来添加到JavaScript的所有功能-程序员宅基地

技术标签: 脚本语言  java  webgl  dwr  xhtml  

I wrote this article to help you move from pre-ES6 knowledge of JavaScript and get you quickly up to speed with the most recent advancements of the language.

我写这篇文章是为了帮助您摆脱ES6之前JavaScript知识,并Swift掌握该语言的最新进展。

JavaScript today is in the privileged position to be the only language that can run natively in the browser, and is highly integrated and optimized for that.

如今,JavaScript处于特权地位,是唯一可以在浏览器中本地运行的语言,并且对此进行了高度集成和优化。

The future of JavaScript is going to be brilliant. Keeping up with the changes shouldn’t be harder than it already is, and my goal here is to give you a quick yet comprehensive overview of the new stuff available to us.

JavaScript的未来将是辉煌的。 跟上变化的难度不应该比现在更难,我在这里的目标是为您提供对我们可用的新内容的快速而全面的概述。

Click here to get a PDF / ePub / Mobi version of this post to read offline

点击此处获取该帖子的PDF / ePub / Mobi版本以离线阅读

ECMAScript简介 (Introduction to ECMAScript)

Whenever you read about JavaScript you’ll inevitably see one of these terms: ES3, ES5, ES6, ES7, ES8, ES2015, ES2016, ES2017, ECMAScript 2017, ECMAScript 2016, ECMAScript 2015… what do they mean?

每当您阅读有关JavaScript的内容时,都会不可避免地看到以下术语之一:ES3,ES5,ES6,ES7,ES8,ES2015,ES2016,ES2017,ECMAScript 2017,ECMAScript 2016,ECMAScript 2015…是什么意思?

They are all referring to a standard, called ECMAScript.

它们都是指称为ECMAScript的标准

ECMAScript is the standard upon which JavaScript is based, and it’s often abbreviated to ES.

ECMAScript是JavaScript所基于的标准 ,并且通常缩写为ES

Beside JavaScript, other languages implement(ed) ECMAScript, including:

除JavaScript外,其他语言也实现ECMAScript,包括:

  • ActionScript (the Flash scripting language), which is losing popularity since Flash will be officially discontinued in 2020

    由于Flash将于2020年正式停产, ActionScript (Flash脚本语言)正在逐渐消失

  • JScript (the Microsoft scripting dialect), since at the time JavaScript was supported only by Netscape and the browser wars were at their peak, Microsoft had to build its own version for Internet Explorer

    JScript (Microsoft脚本方言),因为当时只有Netscape支持JavaScript,并且浏览器之战已达到顶峰,所以Microsoft必须为Internet Explorer构建自己的版本

but of course JavaScript is the most popular and widely used implementation of ES.

但是,当然,JavaScript是ES中最流行和使用广泛的实现。

Why this weird name? Ecma International is a Swiss standards association who is in charge of defining international standards.

为什么这么奇怪的名字? Ecma International是瑞士标准协会,负责定义国际标准。

When JavaScript was created, it was presented by Netscape and Sun Microsystems to Ecma and they gave it the name ECMA-262 alias ECMAScript.

创建JavaScript时,Netscape和Sun Microsystems将其提供给Ecma,他们给它起了ECMA-262别名ECMAScript的名称。

This press release by Netscape and Sun Microsystems (the maker of Java) might help figure out the name choice, which might include legal and branding issues by Microsoft which was in the committee, according to Wikipedia.

Wikipedia称 ,Netscape和Sun Microsystems (Java的制造商)的本新闻稿可能有助于弄清名称的选择,其中可能包括委员会中的Microsoft的法律和品牌问题。

After IE9, Microsoft stopped branding its ES support in browsers as JScript and started calling it JavaScript (at least, I could not find references to it any more).

IE9之后,Microsoft停止在浏览器中将其对ES支持的品牌标识为JScript,并开始将其称为JavaScript(至少,我再也找不到它的引用了)。

So as of 201x, the only popular language supporting the ECMAScript spec is JavaScript.

因此,从201x开始,支持ECMAScript规范的唯一流行语言是JavaScript。

当前ECMAScript版本 (Current ECMAScript version)

The current ECMAScript version is ES2018.

当前的ECMAScript版本是ES2018

It was released in June 2018.

它于2018年6月发布。

什么是TC39 (What is TC39)

TC39 is the committee that evolves JavaScript.

TC39是开发JavaScript的委员会。

The members of TC39 are companies involved in JavaScript and browser vendors, including Mozilla, Google, Facebook, Apple, Microsoft, Intel, PayPal, SalesForce and others.

TC39的成员是参与JavaScript和浏览器供应商的公司,包括Mozilla,Google,Facebook,Apple,Microsoft,Intel,PayPal,SalesForce等。

Every standard version proposal must go through various stages, which are explained here.

每个标准版本建议都必须经历各个阶段,在此进行解释

ES版本 (ES Versions)

I found it puzzling why sometimes an ES version is referenced by edition number and sometimes by year, and I am confused by the year by chance being -1 on the number, which adds to the general confusion around JS/ES ?

我感到困惑,为什么有时会用版本号有时又用年份来引用ES版本,而我对年份却感到困惑,因为偶然的数字是-1,这使人们对JS / ES感到困惑。

Before ES2015, ECMAScript specifications were commonly called by their edition. So ES5 is the official name for the ECMAScript specification update published in 2009.

在ES2015之前,ECMAScript规范通常由其版本调用。 因此,ES5是2009年发布的ECMAScript规范更新的正式名称。

Why does this happen? During the process that led to ES2015, the name was changed from ES6 to ES2015, but since this was done late, people still referenced it as ES6, and the community has not left the edition naming behind — the world is still calling ES releases by edition number.

为什么会这样? 在导致ES2015的过程中,名称从ES6更改为ES2015,但由于此操作进行得很晚,因此人们仍将其称为ES6,并且社区也没有留下版本名称的名称- 世界各地仍在通过以下方式调用ES版本:版本号

This table should clear things up a bit:

该表应将其清除:

Let’s dive into the specific features added to JavaScript since ES5. Let’s start with the ES2015 features.

让我们深入探讨自ES5以来添加到JavaScript的特定功能。 让我们从ES2015功能开始。

let和const (let and const)

Until ES2015, var was the only construct available for defining variables.

在ES2015之前, var是唯一可用于定义变量的构造。

var a = 0

If you forget to add var you will be assigning a value to an undeclared variable, and the results might vary.

如果您忘记添加var ,则将为一个未声明的变量分配一个值,结果可能会有所不同。

In modern environments, with strict mode enabled, you will get an error. In older environments (or with strict mode disabled) this will initialize the variable and assign it to the global object.

在现代环境中,启用严格模式后,您将得到一个错误。 在较旧的环境中(或禁用了严格模式),这将初始化变量并将其分配给全局对象。

If you don’t initialize the variable when you declare it, it will have the undefined value until you assign a value to it.

如果在声明变量时未对其进行初始化,则该变量将具有undefined值,直到您为其分配值为止。

var a //typeof a === 'undefined'

You can redeclare the variable many times, overriding it:

您可以多次声明变量,并将其覆盖:

var a = 1
var a = 2

You can also declare multiple variables at once in the same statement:

您还可以在同一条语句中一次声明多个变量:

var a = 1, b = 2

The scope is the portion of code where the variable is visible.

作用域是代码中可见变量的部分。

A variable initialized with var outside of any function is assigned to the global object, has a global scope and is visible everywhere. A variable initialized with var inside a function is assigned to that function, it's local and is visible only inside it, just like a function parameter.

在任何函数外部用var初始化的变量都分配给全局对象,具有全局作用域,并且随处可见。 将在函数内部使用var初始化的变量分配给该函数,该变量是局部变量,仅在函数内部可见,就像函数参数一样。

Any variable defined in a function with the same name as a global variable takes precedence over the global variable, shadowing it.

与全局变量同名的函数中定义的任何变量都优先于全局变量,并对其进行阴影处理。

It’s important to understand that a block (identified by a pair of curly braces) does not define a new scope. A new scope is only created when a function is created, because var does not have block scope, but function scope.

重要的是要理解一个块(由一对花括号标识)不会定义新的作用域。 仅当创建函数时才创建新作用域,因为var没有块作用域,而是函数作用域。

Inside a function, any variable defined in it is visible throughout all the function code, even if the variable is declared at the end of the function it can still be referenced in the beginning, because JavaScript before executing the code actually moves all variables on top (something that is called hoisting). To avoid confusion, always declare variables at the beginning of a function.

在函数内部,定义在其中的任何变量在整个函数代码中都是可见的,即使在函数的末尾声明了该变量,仍然可以在开始时对其进行引用,因为执行代码之前JavaScript实际上将所有变量移到了顶部 (称为吊装 )。 为避免混淆,请始终在函数的开头声明变量。

使用let (Using let)

let is a new feature introduced in ES2015 and it's essentially a block scoped version of var. Its scope is limited to the block, statement or expression where it's defined, and all the contained inner blocks.

let是ES2015中引入的新功能,本质上是var的块范围版本。 它的范围仅限于定义它的块,语句或表达式,以及所有包含的内部块。

Modern JavaScript developers might choose to only use let and completely discard the use of var.

现代JavaScript开发人员可能会选择只使用let而完全放弃使用var

If let seems an obscure term, just read let color = 'red' as let the color be red and it all makes much more sense

如果let看起来晦涩难懂,请读let color = 'red'因为让颜色为红色,这一切都更有意义

Defining let outside of any function - contrary to var - does not create a global variable.

var相反,在任何函数之外定义let不会创建全局变量。

使用const (Using const)

Variables declared with var or let can be changed later on in the program, and reassigned. Once a const is initialized, its value can never be changed again, and it can't be reassigned to a different value.

varlet声明的变量可以稍后在程序中进行更改,然后重新分配。 const初始化后,其值将永远无法再更改,并且无法将其重新分配给其他值。

const a = 'test'

We can’t assign a different literal to the a const. We can however mutate a if it's an object that provides methods that mutate its contents.

我们不能a const分配其他文字。 然而,我们可以变异a ,如果它是提供了变异的内容方法的对象。

const does not provide immutability, just makes sure that the reference can't be changed.

const不提供不变性,只是确保引用不能更改。

const has block scope, same as let.

const具有块范围,与let相同。

Modern JavaScript developers might choose to always use const for variables that don't need to be reassigned later in the program, because we should always use the simplest construct available to avoid making errors down the road.

现代JavaScript开发人员可能会选择对所有不需要在程序稍后重新分配的变量始终使用const ,因为我们应该始终使用可用的最简单的结构,以避免在以后出错。

箭头功能 (Arrow Functions)

Arrow functions, since their introduction, changed forever how JavaScript code looks (and works).

自从引入箭头功能以来,永远改变了JavaScript代码的外观(和工作方式)。

In my opinion this change was so welcome that you now rarely see the usage of the function keyword in modern codebases. Although that has still its usage.

在我看来,这种更改非常受欢迎,以至于您现在几乎看不到在现代代码库中使用function关键字的情况。 尽管那仍然有它的用法。

Visually, it’s a simple and welcome change, which allows you to write functions with a shorter syntax, from:

从视觉上看,这是一个简单而值得欢迎的更改,它使您可以使用较短的语法编写函数,这些函数来自:

const myFunction = function() {
  //...
}

to

const myFunction = () => {
  //...
}

If the function body contains just a single statement, you can omit the brackets and write all on a single line:

如果函数主体仅包含一条语句,则可以省略方括号并将所有内容写在一行上:

const myFunction = () => doSomething()

Parameters are passed in the parentheses:

参数在括号中传递:

const myFunction = (param1, param2) => doSomething(param1, param2)

If you have one (and just one) parameter, you could omit the parentheses completely:

如果有一个(只有一个)参数,则可以完全省略括号:

const myFunction = param => doSomething(param)

Thanks to this short syntax, arrow functions encourage the use of small functions.

由于这种简短的语法,箭头函数鼓励使用小函数

隐式回报 (Implicit return)

Arrow functions allow you to have an implicit return: values are returned without having to use the return keyword.

箭头函数使您可以隐式返回:无需使用return关键字即可返回值。

It works when there is a one-line statement in the function body:

当函数体中有单行语句时,它可以工作:

const myFunction = () => 'test'

myFunction() //'test'

Another example, when returning an object, remember to wrap the curly brackets in parentheses to avoid it being considered the wrapping function body brackets:

另一个示例,在返回对象时,请记住将花括号括在括号中,以避免将其视为包裹函数的主体括号:

const myFunction = () => ({ value: 'test' })

myFunction() //{value: 'test'}

如何this作品箭头功能 (How this works in arrow functions)

this is a concept that can be complicated to grasp, as it varies a lot depending on the context and also varies depending on the mode of JavaScript (strict mode or not).

this是一个很难理解的概念,因为它随上下文而变化很大,并且还取决于JavaScript的模式(是否为严格模式 )。

It’s important to clarify this concept because arrow functions behave very differently compared to regular functions.

弄清这个概念很重要,因为箭头函数的行为与常规函数非常不同。

When defined as a method of an object, in a regular function this refers to the object, so you can do:

当定义为对象的方法时,在常规函数中, this引用该对象,因此您可以执行以下操作:

const car = {
  model: 'Fiesta',
  manufacturer: 'Ford',
  fullName: function() {
    return `${this.manufacturer} ${this.model}`
  }
}

calling car.fullName() will return "Ford Fiesta".

调用car.fullName()将返回"Ford Fiesta"

The this scope with arrow functions is inherited from the execution context. An arrow function does not bind this at all, so its value will be looked up in the call stack, so in this code car.fullName() will not work, and will return the string "undefined undefined":

this箭头的功能范围从执行上下文继承 。 箭头函数根本不会绑定this函数,因此将在调用堆栈中查找其值,因此在此代码中car.fullName()将不起作用,并将返回字符串"undefined undefined"

const car = {
  model: 'Fiesta',
  manufacturer: 'Ford',
  fullName: () => {
    return `${this.manufacturer} ${this.model}`
  }
}

Due to this, arrow functions are not suited as object methods.

因此,箭头功能不适合作为对象方法。

Arrow functions cannot be used as constructors either, when instantiating an object will raise a TypeError.

箭头函数也不能用作构造函数,在实例化对象时会引发TypeError

This is where regular functions should be used instead, when dynamic context is not needed.

当不需要动态上下文时 ,应在此处使用常规函数代替。

This is also a problem when handling events. DOM Event listeners set this to be the target element, and if you rely on this in an event handler, a regular function is necessary:

在处理事件时,这也是一个问题。 DOM事件监听器设置this是目标元素,如果依靠this在事件处理程序,一个普通的功能是必要的:

const link = document.querySelector('#link')
link.addEventListener('click', () => {
  // this === window
})

const link = document.querySelector('#link')
link.addEventListener('click', function() {
  // this === link
})

班级 (Classes)

JavaScript has quite an uncommon way to implement inheritance: prototypical inheritance. Prototypal inheritance, while in my opinion great, is unlike most other popular programming language’s implementation of inheritance, which is class-based.

JavaScript有一种非常不常见的实现继承的方法:原型继承。 在我看来, 原型继承虽然很棒,但与大多数其他流行的编程语言基于类的继承不同。

People coming from Java or Python or other languages had a hard time understanding the intricacies of prototypal inheritance, so the ECMAScript committee decided to sprinkle syntactic sugar on top of prototypical inheritance so that it resembles how class-based inheritance works in other popular implementations.

来自Java,Python或其他语言的人们很难理解原型继承的复杂性,因此ECMAScript委员会决定在原型继承的基础上撒上语法糖,以使其类似于基于类的继承在其他流行实现中的工作方式。

This is important: JavaScript under the hood is still the same, and you can access an object prototype in the usual way.

这很重要:底层JavaScript仍然相同,并且您可以以通常的方式访问对象原型。

类定义 (A class definition)

This is how a class looks.

这是一个类的外观。

class Person {
  constructor(name) {
    this.name = name
  }
  
  hello() {
    return 'Hello, I am ' + this.name + '.'
  }
}

A class has an identifier, which we can use to create new objects using new ClassIdentifier().

一个类具有一个标识符,我们可以使用它使用new ClassIdentifier()创建新对象。

When the object is initialized, the constructor method is called, with any parameters passed.

初始化对象时,将调用传递任何参数的constructor方法。

A class also has as many methods as it needs. In this case hello is a method and can be called on all objects derived from this class:

一个类还具有所需的许多方法。 在这种情况下, hello是一种方法,可以在派生自此类的所有对象上调用:

const flavio = new Person('Flavio')
flavio.hello()

类继承 (Class inheritance)

A class can extend another class, and objects initialized using that class inherit all the methods of both classes.

一个类可以扩展另一个类,并且使用该类初始化的对象将继承这两个类的所有方法。

If the inherited class has a method with the same name as one of the classes higher in the hierarchy, the closest method takes precedence:

如果继承的类具有与层次结构中较高级别的类之一名称相同的方法,则最接近的方法优先:

class Programmer extends Person {
  hello() {
    return super.hello() + ' I am a programmer.'
  }
}

const flavio = new Programmer('Flavio')
flavio.hello()

(the above program prints “Hello, I am Flavio. I am a programmer.”)

(上面的程序打印“ 您好,我是Flavio。我是程序员。 ”)

Classes do not have explicit class variable declarations, but you must initialize any variable in the constructor.

类没有显式的类变量声明,但是必须在构造函数中初始化任何变量。

Inside a class, you can reference the parent class calling super().

在类内部,可以引用父类调用super()

静态方法 (Static methods)

Normally methods are defined on the instance, not on the class.

通常,方法是在实例上定义的,而不是在类上定义的。

Static methods are executed on the class instead:

静态方法改为在类上执行:

class Person {
  static genericHello() {
    return 'Hello'
  }
}

Person.genericHello() //Hello

私人方法 (Private methods)

JavaScript does not have a built-in way to define private or protected methods.

JavaScript没有内置的方法来定义私有或受保护的方法。

There are workarounds, but I won’t describe them here.

有解决方法,但在此不再赘述。

吸气剂和二传手 (Getters and setters)

You can add methods prefixed with get or set to create a getter and setter, which are two different pieces of code that are executed based on what you are doing: accessing the variable, or modifying its value.

您可以添加以getset为前缀的方法来创建getter和setter,这是基于您正在执行的操作的两个不同的代码段:访问变量或修改其值。

class Person {
  constructor(name) {
    this._name = name
  }
  
  set name(value) {
    this._name = value
  }
  
  get name() {
    return this._name
  }
}

If you only have a getter, the property cannot be set, and any attempt at doing so will be ignored:

如果您只有吸气剂,则无法设置该属性,并且这样做的任何尝试都将被忽略:

class Person {
  constructor(name) {
    this._name = name
  }
  
  get name() {
    return this._name
  }
}

If you only have a setter, you can change the value but not access it from the outside:

如果只有二传手,则可以更改值,但不能从外部访问它:

class Person {
  constructor(name) {
    this._name = name
  }
  
  set name(value) {
    this._name = value
  }
}

默认参数 (Default parameters)

This is a doSomething function which accepts param1.

这是一个接受参数param1doSomething函数。

const doSomething = (param1) => {

}

We can add a default value for param1 if the function is invoked without specifying a parameter:

如果在不指定参数的情况下调用函数,我们可以为param1添加默认值:

const doSomething = (param1 = 'test') => {

}

This works for more parameters as well, of course:

当然,这也适用于更多参数:

const doSomething = (param1 = 'test', param2 = 'test2') => {

}

What if you have an unique object with parameters values in it?

如果您有一个带有参数值的唯一对象怎么办?

Once upon a time, if we had to pass an object of options to a function, in order to have default values of those options if one of them was not defined, you had to add a little bit of code inside the function:

曾几何时,如果我们必须将选项对象传递给函数,如果未定义其中一个选项,则要具有这些选项的默认值,则必须在函数内部添加一些代码:

const colorize = (options) => {
  if (!options) {
    options = {}
  }
  
  const color = ('color' in options) ? options.color : 'yellow'
  ...
}

With destructuring you can provide default values, which simplifies the code a lot:

通过解构,您可以提供默认值,从而大大简化了代码:

const colorize = ({ color = 'yellow' }) => {
  ...
}

If no object is passed when calling our colorize function, similarly we can assign an empty object by default:

如果在调用我们的colorize函数时没有传递任何对象,则类似地,我们可以默认分配一个空对象:

const spin = ({ color = 'yellow' } = {}) => {
  ...
}

模板文字 (Template Literals)

Template Literals allow you to work with strings in a novel way compared to ES5 and below.

与ES5及以下版本相比,模板文字使您可以以新颖的方式使用字符串。

The syntax at a first glance is very simple, just use backticks instead of single or double quotes:

乍一看语法非常简单,只需使用反引号代替单引号或双引号即可:

const a_string = `something`

They are unique because they provide a lot of features that normal strings built with quotes do not, in particular:

它们之所以独特是因为它们提供了很多用引号构建的普通字符串所没有的功能,特别是:

  • they offer a great syntax to define multiline strings

    它们提供了定义多行字符串的绝佳语法
  • they provide an easy way to interpolate variables and expressions in strings

    它们提供了一种简单的方法来对字符串中的变量和表达式进行插值
  • they allow you to create DSLs with template tags (DSL means domain specific language, and it’s for example used in React by Styled Components, to define CSS for a component)

    它们允许您使用模板标签创建DSL(DSL表示域特定的语言,例如,在React by Styled Components中使用,以为组件定义CSS)

Let’s dive into each of these in detail.

让我们详细研究每个。

多行字符串 (Multiline strings)

Pre-ES6, to create a string spanning over two lines you had to use the \ character at the end of a line:

在ES6之前的版本中,要创建跨越两行的字符串,您必须在行末使用\字符:

const string =
  'first part \
second part'

This allows to create a string on 2 lines, but it’s rendered on just one line:

这允许在两行上创建一个字符串,但是仅在一行上呈现:

first part second part

first part second part

To render the string on multiple lines as well, you explicitly need to add \n at the end of each line, like this:

要同时在多行上呈现字符串,您需要明确地在每行的末尾添加\n ,如下所示:

const string =
  'first line\n \
second line'

or

要么

const string = 'first line\n' + 'second line'

Template literals make multiline strings much simpler.

模板文字使多行字符串更简单。

Once a template literal is opened with the backtick, you just press enter to create a new line, with no special characters, and it’s rendered as-is:

使用反引号打开模板文字后,只需按Enter键即可创建新行(不包含任何特殊字符),并按原样呈现:

const string = `Hey
this

string
is awesome!`

Keep in mind that space is meaningful, so doing this:

请记住,空间是有意义的,因此请执行以下操作:

const string = `First
                Second`

is going to create a string like this:

将创建这样的字符串:

First
                Second

an easy way to fix this problem is by having an empty first line, and appending the trim() method right after the closing backtick, which will eliminate any space before the first character:

解决此问题的一种简单方法是,在第一行为空,并在结束反引号之后附加trim()方法,这将消除第一个字符之前的任何空格:

const string = `
First
Second`.trim()

插补 (Interpolation)

Template literals provide an easy way to interpolate variables and expressions into strings.

模板文字提供了一种将变量和表达式内插到字符串中的简便方法。

You do so by using the ${...} syntax:

您可以使用${...}语法来实现:

const var = 'test'
const string = `something ${var}` //something test

inside the ${} you can add anything, even expressions:

${}您可以添加任何内容,甚至是表达式:

const string = `something ${1 + 2 + 3}`
const string2 = `something ${foo() ? 'x' : 'y'}`

模板标签 (Template tags)

Tagged templates is one feature that might sound less useful at first for you, but it’s actually used by lots of popular libraries around, like Styled Components or Apollo, the GraphQL client/server lib, so it’s essential to understand how it works.

标记模板是一个功能,乍一看对您来说似乎没什么用,但是实际上它被许多流行的库使用,例如Styled Components或GraphQL客户/服务器库Apollo,因此了解它的工作原理非常重要。

In Styled Components template tags are used to define CSS strings:

在“样式化组件”中,模板标记用于定义CSS字符串:

const Button = styled.button`
  font-size: 1.5em;
  background-color: black;
  color: white;
`

In Apollo template tags are used to define a GraphQL query schema:

在Apollo中,模板标记用于定义GraphQL查询架构:

const query = gql`
  query {
    ...
  }
`

The styled.button and gql template tags highlighted in those examples are just functions:

这些示例中突出显示的styled.buttongql模板标签只是函数

function gql(literals, ...expressions) {}

this function returns a string, which can be the result of any kind of computation.

此函数返回一个字符串,该字符串可以是任何类型的计算的结果。

literals is an array containing the template literal content tokenized by the expressions interpolations.

literals是一个数组,其中包含由表达式插值标记的模板文字内容。

expressions contains all the interpolations.

expressions包含所有插值。

If we take an example above:

如果我们以上述示例为例:

const string = `something ${1 + 2 + 3}`

literals is an array with two items. The first is something, the string until the first interpolation, and the second is an empty string, the space between the end of the first interpolation (we only have one) and the end of the string.

literals是具有两个项目的数组。 第一个是something ,直到第一个插值为止的字符串,第二个是空字符串,即第一个插值的结尾(我们只有一个)和字符串的结尾之间的空格。

expressions in this case is an array with a single item, 6.

在这种情况下, expressions是具有单个项目6的数组。

A more complex example is:

一个更复杂的示例是:

const string = `something
another ${'x'}
new line ${1 + 2 + 3}
test`

in this case literals is an array where the first item is:

在这种情况下, literals是一个数组,其中第一项是:

;`something
another `

the second is:

第二个是:

;`new line `

and the third is:

第三是:

;`
new line `

expressions in this case is an array with two items, x and 6.

在这种情况下, expressions是一个具有两项x6的数组。

The function that is passed those values can do anything with them, and this is the power of this kind feature.

传递这些值的函数可以对它们执行任何操作,而这正是此类功能的强大功能。

The most simple example is replicating what the string interpolation does, by joining literals and expressions:

最简单的示例是通过连接literalsexpressions来复制字符串插值的expressions

const interpolated = interpolate`I paid ${10}€`

and this is how interpolate works:

这是interpolate工作方式:

function interpolate(literals, ...expressions) {
  let string = ``
  for (const [i, val] of expressions) {
    string += literals[i] + val
  }
  string += literals[literals.length - 1]
  return string
}

销毁工作 (Destructuring assignments)

Given an object, you can extract just some values and put them into named variables:

给定一个对象,您可以仅提取一些值并将其放入命名变量中:

const person = {
  firstName: 'Tom',
  lastName: 'Cruise',
  actor: true,
  age: 54, //made up
}

const {firstName: name, age} = person

name and age contain the desired values.

nameage包含所需的值。

The syntax also works on arrays:

该语法也适用于数组:

const a = [1,2,3,4,5]
const [first, second] = a

This statement creates 3 new variables by getting the items with index 0, 1, 4 from the array a:

该语句通过从数组a获取索引为0、1、4的项来创建3个新变量:

增强的对象文字 (Enhanced Object Literals)

const [first, second, , , fifth] = a

In ES2015 Object Literals gained superpowers.

在ES2015中,Object Literals获得了超能力。

包含变量的语法更简单 (Simpler syntax to include variables)

Instead of doing

而不是做

const something = 'y'
const x = {
  something: something
}

you can do

你可以做

const something = 'y'
const x = {
  something
}

原型 (Prototype)

A prototype can be specified with

原型可以用

const anObject = { y: 'y' }
const x = {
  __proto__: anObject
}

超() (super())

const anObject = { y: 'y', test: () => 'zoo' }
const x = {
  __proto__: anObject,
  test() {
    return super.test() + 'x'
  }
}
x.test() //zoox

动态特性 (Dynamic properties)

const x = {
  ['a' + '_' + 'b']: 'z'
}
x.a_b //z

循环循环 (For-of loop)

ES5 back in 2009 introduced forEach() loops. While nice, they offered no way to break, like for loops always did.

早在2009年,ES5就引入了forEach()循环。 虽然不错,但它们没有提供中断的方法,就像for循环总是一样。

ES2015 introduced the for-of loop, which combines the conciseness of forEach with the ability to break:

ES2015引入了for-of 循环 ,该循环结合了forEach的简洁性和打破能力:

//iterate over the value
for (const v of ['a', 'b', 'c']) {
  console.log(v);
}

//get the index as well, using `entries()`
for (const [i, v] of ['a', 'b', 'c'].entries()) {
  console.log(index) //index
  console.log(value) //value
}

Notice the use of const. This loop creates a new scope in every iteration, so we can safely use that instead of let.

注意const的使用。 这个循环在每次迭代中都会创建一个新的作用域,因此我们可以放心地使用它代替let

The difference with for...in is:

for...in的区别是:

  • for...of iterates over the property values

    for...of 迭代属性值

  • for...in iterates the property names

    for...in 迭代属性名称

承诺 (Promises)

A promise is commonly defined as a proxy for a value that will eventually become available.

通常将promise定义为最终将变为可用值的代理

Promises are one way to deal with asynchronous code, without writing too many callbacks in your code.

承诺是处理异步代码的一种方法,而无需在代码中编写过多的回调。

Async functions use the promises API as their building block, so understanding them is fundamental even if in newer code you’ll likely use async functions instead of promises.

异步函数将promise API用作其构建块,因此即使在较新的代码中您可能会使用异步函数代替promise,对它们的理解也是基础。

简而言之,诺言如何运作 (How promises work, in brief)

Once a promise has been called, it will start in pending state. This means that the caller function continues the execution, while it waits for the promise to do its own processing, and give the caller function some feedback.

承诺被调用后,它将以待处理状态开始。 这意味着调用方函数继续执行,同时等待promise进行自己的处理,并向调用方函数提供一些反馈。

At this point, the caller function waits for it to either return the promise in a resolved state, or in a rejected state, but as you know JavaScript is asynchronous, so the function continues its execution while the promise does it work.

此时,调用者函数等待它以已解决状态或被拒绝状态返回promise,但是您知道JavaScript是异步的,因此该函数在promise起作用的同时继续执行

哪个JS API使用承诺? (Which JS API use promises?)

In addition to your own code and library code, promises are used by standard modern Web APIs such as:

除了您自己的代码和库代码之外,标准的现代Web API还使用了promise,例如:

It’s unlikely that in modern JavaScript you’ll find yourself not using promises, so let’s start diving right into them.

在现代JavaScript中,您不太可能会发现自己没有使用Promise,因此让我们开始深入研究它们。

创造承诺 (Creating a promise)

The Promise API exposes a Promise constructor, which you initialize using new Promise():

Promise API公开了一个Promise构造函数,您可以使用new Promise()初始化:

let done = true

const isItDoneYet = new Promise((resolve, reject) => {
  if (done) {
    const workDone = 'Here is the thing I built'
    resolve(workDone)
  } else {
    const why = 'Still working on something else'
    reject(why)
  }
})

As you can see the promise checks the done global constant, and if that's true, we return a resolved promise, otherwise a rejected promise.

如您所见,promise检查已done全局常量,如果是这样,我们将返回已解决的Promise,否则将返回被拒绝的Promise。

Using resolve and reject we can communicate back a value, in the above case we just return a string, but it could be an object as well.

使用resolvereject我们可以返回一个值,在上述情况下,我们只返回一个字符串,但是它也可以是一个对象。

兑现承诺 (Consuming a promise)

In the last section, we introduced how a promise is created.

在上一节中,我们介绍了如何创建承诺。

Now let’s see how the promise can be consumed or used.

现在,让我们来看看如何承诺可以消耗或使用。

const isItDoneYet = new Promise()
//...

const checkIfItsDone = () => {
  isItDoneYet
    .then(ok => {
      console.log(ok)
    })
    .catch(err => {
      console.error(err)
    })
}

Running checkIfItsDone() will execute the isItDoneYet() promise and will wait for it to resolve, using the then callback, and if there is an error, it will handle it in the catch callback.

运行checkIfItsDone()将执行isItDoneYet() ,并使用then回调等待其解决,如果有错误,它将在catch回调中对其进行处理。

连锁承诺 (Chaining promises)

A promise can be returned to another promise, creating a chain of promises.

一个承诺可以返回到另一个承诺,从而创建一个承诺链。

A great example of chaining promises is given by the Fetch API, a layer on top of the XMLHttpRequest API, which we can use to get a resource and queue a chain of promises to execute when the resource is fetched.

链接承诺的一个很好的例子是Fetch API ,它是XMLHttpRequest API之上的一层,我们可以使用它来获取资源,并在获取资源时排队执行承诺。

The Fetch API is a promise-based mechanism, and calling fetch() is equivalent to defining our own promise using new Promise().

Fetch API是一种基于承诺的机制,调用fetch()等效于使用new Promise()定义我们自己的承诺。

链接承诺的示例 (Example of chaining promises)

const status = response => {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  }
  return Promise.reject(new Error(response.statusText))
}

const json = response => response.json()

fetch('/todos.json')
  .then(status)
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data)
  })
  .catch(error => {
    console.log('Request failed', error)
  })

In this example, we call fetch() to get a list of TODO items from the todos.json file found in the domain root, and we create a chain of promises.

在此示例中,我们调用fetch()从域根目录中的todos.json文件中获取TODO项目列表,并创建一个Promise链。

Running fetch() returns a response, which has many properties, and within those we reference:

运行fetch()返回一个response ,它具有许多属性,在我们引用的属性内:

  • status, a numeric value representing the HTTP status code

    status ,一个表示HTTP状态代码的数值

  • statusText, a status message, which is OK if the request succeeded

    statusText ,状态消息,如果请求成功,则OK

response also has a json() method, which returns a promise that will resolve with the content of the body processed and transformed into JSON.

response也有一个json()方法,该方法返回一个promise,该promise将与处理并转换为JSON的正文内容一起解析。

So given those premises, this is what happens: the first promise in the chain is a function that we defined, called status(), that checks the response status and if it's not a success response (between 200 and 299), it rejects the promise.

因此,考虑到这些前提,就会发生这种情况:链中的第一个promise是我们定义的函数,即status() ,它检查响应状态,如果不是成功响应(介于200和299之间),它将拒绝诺言。

This operation will cause the promise chain to skip all the chained promises listed and will skip directly to the catch() statement at the bottom, logging the Request failed text along with the error message.

此操作将导致promise链跳过列出的所有链接的promise,并将直接跳到底部的catch()语句,并记录Request failed文本和错误消息。

If that succeeds instead, it calls the json() function we defined. Since the previous promise, when successful, returned the response object, we get it as an input to the second promise.

如果成功,它将调用我们定义的json()函数。 由于上一个承诺成功后返回了response对象,因此我们将其作为第二个承诺的输入。

In this case, we return the data JSON processed, so the third promise receives the JSON directly:

在这种情况下,我们返回经过JSON处理的数据,因此第三个promise直接接收JSON:

.then((data) => {
  console.log('Request succeeded with JSON response', data)
})

and we log it to the console.

然后将其记录到控制台。

处理错误 (Handling errors)

In the above example, in the previous section, we had a catch that was appended to the chain of promises.

在上面的例子中,上一节中,我们有一个catch这是附加承诺的链条。

When anything in the chain of promises fails and raises an error or rejects the promise, the control goes to the nearest catch() statement down the chain.

当promise链中的任何内容失败并且引发错误或拒绝promise时,控件将转到链中最近的catch()语句。

new Promise((resolve, reject) => {
  throw new Error('Error')
}).catch(err => {
  console.error(err)
})

// or

new Promise((resolve, reject) => {
  reject('Error')
}).catch(err => {
  console.error(err)
})

级联错误 (Cascading errors)

If inside the catch() you raise an error, you can append a second catch() to handle it, and so on.

如果在catch()内部引发错误,则可以附加第二个catch()来处理它,依此类推。

new Promise((resolve, reject) => {
  throw new Error('Error')
})
  .catch(err => {
    throw new Error('Error')
  })
  .catch(err => {
    console.error(err)
  })

编排承诺 (Orchestrating promises)

Promise.all() (Promise.all())

If you need to synchronize different promises, Promise.all() helps you define a list of promises, and execute something when they are all resolved.

如果您需要同步不同的Promise.all()Promise.all()可帮助您定义一个Promise.all()列表,并在它们全部解决后执行一些操作。

Example:

例:

const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')

Promise.all([f1, f2])
  .then(res => {
    console.log('Array of results', res)
  })
  .catch(err => {
    console.error(err)
  })

The ES2015 destructuring assignment syntax allows you to also do

ES2015解构分配语法使您还可以

Promise.all([f1, f2]).then(([res1, res2]) => {
  console.log('Results', res1, res2)
})

You are not limited to using fetch of course, any promise is good to go.

当然,您不仅限于使用fetch任何承诺都是可以的

Promise.race() (Promise.race())

Promise.race() runs as soon as one of the promises you pass to it resolves, and it runs the attached callback just once with the result of the first promise resolved.

Promise.race()在您传递给它的一个诺言解析后立即运行,并且在第一个诺言得到解决的情况下,它仅运行附加的回调一次。

Example:

例:

const promiseOne = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one')
})
const promiseTwo = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two')
})

Promise.race([promiseOne, promiseTwo]).then(result => {
  console.log(result) // 'two'
})

模组 (Modules)

ES Modules is the ECMAScript standard for working with modules.

ES模块是用于模块的ECMAScript标准。

While Node.js has been using the CommonJS standard for years, the browser never had a module system, as every major decision such as a module system must be first standardized by ECMAScript and then implemented by the browser.

尽管Node.js多年来一直使用CommonJS标准,但浏览器从未拥有模块系统,因为诸如模块系统之类的每个主要决策都必须首先由ECMAScript标准化,然后由浏览器实现。

This standardization process completed with ES2015 and browsers started implementing this standard trying to keep everything well aligned, working all in the same way, and now ES Modules are supported in Chrome, Safari, Edge and Firefox (since version 60).

该标准化过程已在ES2015中完成,浏览器开始实施此标准,以使所有内容保持一致,并以相同的方式工作,现在Chrome,Safari,Edge和Firefox(版本60开始)均支持ES模块。

Modules are very cool, because they let you encapsulate all sorts of functionality, and expose this functionality to other JavaScript files, as libraries.

模块非常酷,因为它们使您可以封装各种功能,并将该功能作为库公开给其他JavaScript文件。

ES模块语法 (The ES Modules Syntax)

The syntax to import a module is:

导入模块的语法为:

import package from 'module-name'

while CommonJS uses

而CommonJS使用

const package = require('module-name')

A module is a JavaScript file that exports one or more values (objects, functions or variables), using the export keyword. For example, this module exports a function that returns a string uppercase:

模块是一个JavaScript文件,该文件使用export关键字导出一个或多个值(对象,函数或变量)。 例如,此模块导出一个返回大写字符串的函数:

uppercase.js

uppercase.js

export default str => str.toUpperCase()

In this example, the module defines a single, default export, so it can be an anonymous function. Otherwise it would need a name to distinguish it from other exports.

在此示例中,模块定义了一个默认的export ,因此它可以是匿名函数。 否则,将需要一个名称来区别于其他出口。

Now, any other JavaScript module can import the functionality offered by uppercase.js by importing it.

现在, 任何其他JavaScript模块都可以通过导入来导入uppercase.js提供的功能。

An HTML page can add a module by using a <script> tag with the special type="module" attribute:

HTML页面可以通过使用具有ecial type="m odule”属性的<scri pt>标记来添加模块:

<script type="module" src="index.js"></script>

Note: this module import behaves like a defer script load. See efficiently load JavaScript with defer and async

注意:此模块导入的行为类似于defer脚本加载。 通过延迟和异步查看有效加载JavaScript

It’s important to note that any script loaded with type="module" is loaded in strict mode.

重要的是要注意,任何以type="module"加载的脚本都以严格模式加载。

In this example, the uppercase.js module defines a default export, so when we import it, we can assign it a name we prefer:

在此示例中, uppercase.js模块定义了默认的export ,因此,当我们导入它时,我们可以为其指定一个我们更喜欢的名称:

import toUpperCase from './uppercase.js'

and we can use it:

我们可以使用它:

toUpperCase('test') //'TEST'

You can also use an absolute path for the module import, to reference modules defined on another domain:

您还可以使用绝对路径进行模块导入,以引用在另一个域上定义的模块:

import toUpperCase from 'https://flavio-es-modules-example.glitch.me/uppercase.js'

This is also valid import syntax:

这也是有效的导入语法:

import { toUpperCase } from '/uppercase.js'
import { toUpperCase } from '../uppercase.js'

This is not:

这不是:

import { toUpperCase } from 'uppercase.js'
import { toUpperCase } from 'utils/uppercase.js'

It’s either absolute, or has a ./ or / before the name.

它可以是绝对的,也可以在名称前带有.//

其他导入/导出选项 (Other import/export options)

We saw this example above:

我们在上面看到了这个例子:

export default str => str.toUpperCase()

This creates one default export. In a file however you can export more than one thing, by using this syntax:

这将创建一个默认导出。 但是,在文件中,可以使用以下语法导出多个内容:

const a = 1
const b = 2
const c = 3

export { a, b, c }

Another module can import all those exports using

另一个模块可以使用

import * from 'module'

You can import just a few of those exports, using the destructuring assignment:

使用解构分配,您可以仅导入其中一些导出:

import { a } from 'module'
import { a, b } from 'module'

You can rename any import, for convenience, using as:

您可以重命名任何进口,为方便起见,使用as

import { a, b as two } from 'module'

You can import the default export, and any non-default export by name, like in this common React import:

您可以导入默认导出,也可以按名称导入任何非默认导出,例如在此常见的React导入中:

import React, { Component } from 'react'

You can see an ES Modules example here: https://glitch.com/edit/#!/flavio-es-modules-example?path=index.html

您可以在此处看到ES模块示例: https : //glitch.com/edit/#!/ flavio-es-modules-example ?path=index.html

CORS (CORS)

Modules are fetched using CORS. This means that if you reference scripts from other domains, they must have a valid CORS header that allows cross-site loading (like Access-Control-Allow-Origin: *)

使用CORS提取模块。 这意味着,如果您引用其他域中的脚本,则它们必须具有有效的CORS标头,以允许跨站点加载(例如Access-Control-Allow-Origin: * )。

不支持模块的浏览器呢? (What about browsers that do not support modules?)

Use a combination of type="module" and nomodule:

使用type="module"nomodule

<script type="module" src="module.js"></script>
<script nomodule src="fallback.js"></script>

包装模块 (Wrapping up modules)

ES Modules are one of the biggest features introduced in modern browsers. They are part of ES6 but the road to implement them has been long.

ES模块是现代浏览器中引入的最大功能之一。 它们是ES6的一部分,但是实现它们的路很长。

We can now use them! But we must also remember that having more than a few modules is going to have a performance hit on our pages, as it’s one more step that the browser must perform at runtime.

我们现在可以使用它们了! 但是我们还必须记住,拥有多个模块将对我们的页面产生性能影响,因为这是浏览器在运行时必须执行的又一步。

Webpack is probably going to still be a huge player even if ES Modules land in the browser, but having such a feature directly built in the language is huge for a unification of how modules work client-side and on Node.js as well.

即使ES模块位于浏览器中,Webpack仍将扮演重要角色,但是直接用该语言构建这样的功能对于统一模块在客户端以及在Node.js上的工作方式来说是巨大的。

新的String方法 (New String methods)

Any string value got some new instance methods:

任何字符串值都有一些新的实例方法:

  • repeat()

    repeat()

  • codePointAt()

    codePointAt()

重复() (repeat())

Repeats the strings for the specified number of times:

将字符串重复指定的次数:

'Ho'.repeat(3) //'HoHoHo'

Returns an empty string if there is no parameter, or the parameter is 0. If the parameter is negative you'll get a RangeError.

如果没有参数,或者参数为0 ,则返回一个空字符串。 如果参数为负,则会出现RangeError。

codePointAt() (codePointAt())

This method can be used to handle Unicode characters that cannot be represented by a single 16-bit Unicode unit, but need 2 instead.

此方法可用于处理无法用单个16位Unicode单元表示但需要2个字符的Unicode字符。

Using charCodeAt() you need to retrieve the first, and the second, and combine them. Using codePointAt() you get the whole character in one call.

使用charCodeAt()您需要检索第一个和第二个,并将它们合并。 使用codePointAt()可以一次调用整个字符。

For example, this Chinese character “” is composed by 2 UTF-16 (Unicode) parts:

例如,此汉字“”由2个UTF-16(Unicode)组成:

"".charCodeAt(0).toString(16) //d842
"".charCodeAt(1).toString(16) //dfb7

If you create a new character by combining those unicode characters:

如果通过组合这些unicode字符来创建新字符:

"\ud842\udfb7" //""

You can get the same result sign codePointAt():

您可以获得相同的结果符号codePointAt()

"".codePointAt(0) //20bb7

If you create a new character by combining those unicode characters:

如果通过组合这些unicode字符来创建新字符:

"\u{20bb7}" //""

More on Unicode and working with it in my Unicode guide.

有关Unicode的更多信息,请参见Unicode指南

新对象方法 (New Object methods)

ES2015 introduced several static methods under the Object namespace:

ES2015在对象名称空间下引入了几种静态方法:

  • Object.is() determines if two values are the same value

    Object.is()确定两个值是否相同

  • Object.assign() used to shallow copy an object

    Object.assign()用于浅复制对象

  • Object.setPrototypeOf sets an object prototype

    Object.setPrototypeOf设置对象原型

Object.is() (Object.is())

This methods aims to help comparing values.

此方法旨在帮助比较值。

Usage:

用法:

Object.is(a, b)

The result is always false unless:

结果始终为false除非:

  • a and b are the same exact object

    ab是相同的精确对象

  • a and b are equal strings (strings are equal when composed by the same characters)

    ab是相等的字符串(当由相同字符组成时,字符串是相等的)

  • a and b are equal numbers (numbers are equal when their value is equal)

    ab是相等的数字(数值相等时数字相等)

  • a and b are both undefined, both null, both NaN, both true or both false

    abundefined ,都为null ,都为NaN ,都为true或均为false

0 and -0 are different values in JavaScript, so pay attention in this special case (convert all to +0 using the + unary operator before comparing, for example).

0-0是JavaScript中的不同值,因此请注意这种特殊情况(例如,在进行比较之前,使用+一元运算符将所有值转换为+0 )。

Object.assign() (Object.assign())

Introduced in ES2015, this method copies all the enumerable own properties of one or more objects into another.

此方法在ES2015ES2015 ,将一个或多个对象的所有可枚举的自身属性复制到另一个对象中。

Its primary use case is to create a shallow copy of an object.

它的主要用例是创建对象的浅表副本。

const copied = Object.assign({}, original)

Being a shallow copy, values are cloned, and objects references are copied (not the objects themselves), so if you edit an object property in the original object, that’s modified also in the copied object, since the referenced inner object is the same:

作为浅表副本,将克隆值并复制对象引用(而不是对象本身),因此,如果在原始对象中编辑对象属性,则在复制的对象中也将对其进行修改,因为引用的内部对象是相同的:

const original = {
  name: 'Fiesta',
  car: {
    color: 'blue'
  }
}

const copied = Object.assign({}, original)

original.name = 'Focus'
original.car.color = 'yellow'

copied.name //Fiesta
copied.car.color //yellow

I mentioned “one or more”:

我提到“一个或多个”:

const wisePerson = {
  isWise: true
}
const foolishPerson = {
  isFoolish: true
}
const wiseAndFoolishPerson = Object.assign({}, wisePerson, foolishPerson)

console.log(wiseAndFoolishPerson) //{ isWise: true, isFoolish: true }

Object.setPrototypeOf() (Object.setPrototypeOf())

Set the prototype of an object. Accepts two arguments: the object and the prototype.

设置对象的原型。 接受两个参数:对象和原型。

Usage:

用法:

Object.setPrototypeOf(object, prototype)

Example:

例:

const animal = {
  isAnimal: true
}
const mammal = {
  isMammal: true
}

mammal.__proto__ = animal
mammal.isAnimal //true

const dog = Object.create(animal)

dog.isAnimal  //true
console.log(dog.isMammal)  //undefined

Object.setPrototypeOf(dog, mammal)

dog.isAnimal //true
dog.isMammal //true

点差运算符 (The spread operator)

You can expand an array, an object or a string using the spread operator ...

您可以使用传播运算符扩展数组,对象或字符串...

Let’s start with an array example. Given

让我们从一个数组示例开始。 给定

const a = [1, 2, 3]

you can create a new array using

您可以使用创建一个新数组

const b = [...a, 4, 5, 6]

You can also create a copy of an array using

您还可以使用创建一个数组的副本

const c = [...a]

This works for objects as well. Clone an object with:

这也适用于对象。 克隆对象:

const newObj = { ...oldObj }

Using strings, the spread operator creates an array with each char in the string:

使用字符串,spread运算符使用字符串中的每个字符创建一个数组:

const hey = 'hey'
const arrayized = [...hey] // ['h', 'e', 'y']

This operator has some pretty useful applications. The most important one is the ability to use an array as function argument in a very simple way:

该运算符具有一些非常有用的应用程序。 最重要的一个功能是以一种非常简单的方式将数组用作函数参数的能力:

(In the past you could do this using f.apply(null, a) but that's not as nice and readable.)

(过去,您可以使用f.apply(null, a)来执行此操作f.apply(null, a)但这并不是那么好读。)

The rest element is useful when working with array destructuring:

在处理数组解构时, rest元素非常有用:

const numbers = [1, 2, 3, 4, 5]
[first, second, ...others] = numbers

and spread elements:

传播元素

const numbers = [1, 2, 3, 4, 5]
const sum = (a, b, c, d, e) => a + b + c + d + e
const sum = sum(...numbers)

ES2018 introduces rest properties, which are the same but for objects.

ES2018引入了其余属性,但对象相同。

Rest properties:

休息性质

const { first, second, ...others } = {
  first: 1,
  second: 2,
  third: 3,
  fourth: 4,
  fifth: 5
}

first // 1
second // 2
others // { third: 3, fourth: 4, fifth: 5 }

Spread properties allow us to create a new object by combining the properties of the object passed after the spread operator:

传播属性允许我们通过组合在传播运算符之后传递的对象的属性来创建新对象:

const items = { first, second, ...others }
items //{ first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }

(Set)

A Set data structure allows us to add data to a container.

Set数据结构使我们可以向容器中添加数据。

A Set is a collection of objects or primitive types (strings, numbers or booleans), and you can think of it as a Map where values are used as map keys, with the map value always being a boolean true.

Set是对象或原始类型(字符串,数字或布尔值)的集合,您可以将其视为Map,其中将值用作映射键,而map值始终为布尔值true。

初始化集合 (Initialize a Set)

A Set is initialized by calling:

通过以下方式初始化Set:

const s = new Set()

将项目添加到集合 (Add items to a Set)

You can add items to the Set by using the add method:

您可以使用add方法将项目添加到Set中:

s.add('one')
s.add('two')

A set only stores unique elements, so calling s.add('one') multiple times won't add new items.

集合仅存储唯一元素,因此s.add('one')调用s.add('one')不会添加新项目。

You can’t add multiple elements to a set at the same time. You need to call add() multiple times.

您不能同时将多个元素添加到集合中。 您需要多次调用add()

检查项目是否在集合中 (Check if an item is in the set)

Once an element is in the set, we can check if the set contains it:

一旦元素进入集合,我们就可以检查集合是否包含它:

s.has('one') //true
s.has('three') //false

从“按键设置”中删除项目 (Delete an item from a Set by key)

Use the delete() method:

使用delete()方法:

s.delete('one')

确定集合中的项目数 (Determine the number of items in a Set)

Use the size property:

使用size属性:

s.size

从集合中删除所有项目 (Delete all items from a Set)

Use the clear() method:

使用clear()方法:

s.clear()

迭代集中的项目 (Iterate the items in a Set)

Use the keys() or values() methods - they are equivalent:

使用keys()values()方法-它们等效:

for (const k of s.keys()) {
  console.log(k)
}

for (const k of s.values()) {
  console.log(k)
}

The entries() method returns an iterator, which you can use like this:

entries()方法返回一个迭代器,您可以像这样使用它:

const i = s.entries()
console.log(i.next())

calling i.next() will return each element as a { value, done = false } object until the iterator ends, at which point done is true.

调用i.next()会将每个元素作为{ value, done = false }对象返回,直到迭代器结束为止,此时donetrue

You can also use the forEach() method on the set:

您还可以在集合上使用forEach()方法:

s.forEach(v => console.log(v))

or you can just use the set in a for..of loop:

或者您也可以在for..of循环中使用该集合:

for (const k of s) {
  console.log(k)
}

用值初始化集合 (Initialize a Set with values)

You can initialize a Set with a set of values:

您可以使用一组值来初始化Set:

const s = new Set([1, 2, 3, 4])

将Set键转换为数组 (Convert the Set keys into an array)

const a = [...s.keys()]

// or

const a = [...s.values()]

弱集 (A WeakSet)

A WeakSet is a special kind of Set.

WeakSet是一种特殊的Set。

In a Set, items are never garbage collected. A WeakSet instead lets all its items be freely garbage collected. Every key of a WeakSet is an object. When the reference to this object is lost, the value can be garbage collected.

在集合中,永远不会垃圾收集项目。 相反,WeakSet允许自由地对其所有项目进行垃圾回收。 WeakSet的每个键都是一个对象。 当对该对象的引用丢失时,可以对该值进行垃圾回收。

Here are the main differences:

主要区别如下:

  1. you cannot iterate over the WeakSet

    您无法遍历WeakSet
  2. you cannot clear all items from a WeakSet

    您无法从WeakSet中清除所有项目
  3. you cannot check its size

    你不能检查它的大小

A WeakSet is generally used by framework-level code, and only exposes these methods:

WeakSet通常由框架级代码使用,并且仅公开以下方法:

  • add()

    加()
  • has()

    有()
  • delete()

    删除()

地图 (Map)

A Map data structure allows us to associate data to a key.

Map数据结构使我们可以将数据关联到键。

在ES6之前 (Before ES6)

Before its introduction, people generally used objects as maps, by associating some object or value to a specific key value:

在引入之前,人们通常通过将某些对象或值与特定的键值相关联来将对象用作地图:

const car = {}
car['color'] = 'red'
car.owner = 'Flavio'
console.log(car['color']) //red
console.log(car.color) //red
console.log(car.owner) //Flavio
console.log(car['owner']) //Flavio

输入地图 (Enter Map)

ES6 introduced the Map data structure, providing us a proper tool to handle this kind of data organization.

ES6引入了Map数据结构,从而为我们提供了处理此类数据组织的适当工具。

A Map is initialized by calling:

通过调用以下方法初始化Map:

const m = new Map()

将项目添加到地图 (Add items to a Map)

You can add items to the map by using the set method:

您可以使用set方法将项目添加到地图:

m.set('color', 'red')
m.set('age', 2)

通过按键从地图上获取物品 (Get an item from a map by key)

And you can get items out of a map by using get:

你可以得到的物品拿出地图通过使用get

const color = m.get('color')
const age = m.get('age')

通过按键从地图上删除项目 (Delete an item from a map by key)

Use the delete() method:

使用delete()方法:

m.delete('color')

从地图上删除所有项目 (Delete all items from a map)

Use the clear() method:

使用clear()方法:

m.clear()

通过键检查地图是否包含项 (Check if a map contains an item by key)

Use the has() method:

使用has()方法:

const hasColor = m.has('color')

查找地图中的项目数 (Find the number of items in a map)

Use the size property:

使用size属性:

const size = m.size

用值初始化地图 (Initialize a map with values)

You can initialize a map with a set of values:

您可以使用一组值初始化地图:

const m = new Map([['color', 'red'], ['owner', 'Flavio'], ['age', 2]])

地图键 (Map keys)

Just like any value (object, array, string, number) can be used as the value of the key-value entry of a map item, any value can be used as the key, even objects.

就像任何值(对象,数组,字符串,数字)都可以用作映射项的键值条目的值一样任何值都可以用作键 (甚至对象)。

If you try to get a non-existing key using get() out of a map, it will return undefined.

如果尝试使用get()从地图中获取不存在的键,则它将返回undefined

在现实生活中几乎找不到的奇怪情况 (Weird situations you’ll almost never find in real life)

const m = new Map()
m.set(NaN, 'test')
m.get(NaN) //test

const m = new Map()
m.set(+0, 'test')
m.get(-0) //test

遍历地图键 (Iterate over map keys)

Map offers the keys() method we can use to iterate on all the keys:

Map提供了我们可以用来迭代所有键的keys()方法:

for (const k of m.keys()) {
  console.log(k)
}

遍历地图值 (Iterate over map values)

The Map object offers the values() method we can use to iterate on all the values:

Map对象提供了values()方法,可用于迭代所有值:

for (const v of m.values()) {
  console.log(v)
}

遍历映射键,值对 (Iterate over map key, value pairs)

The Map object offers the entries() method we can use to iterate on all the values:

Map对象提供了entries()方法,可用于迭代所有值:

for (const [k, v] of m.entries()) {
  console.log(k, v)
}

which can be simplified to

可以简化为

for (const [k, v] of m) {
  console.log(k, v)
}

将映射键转换为数组 (Convert the map keys into an array)

const a = [...m.keys()]

将地图值转换为数组 (Convert the map values into an array)

const a = [...m.values()]

弱地图 (WeakMap)

A WeakMap is a special kind of map.

WeakMap是一种特殊的地图。

In a map object, items are never garbage collected. A WeakMap instead lets all its items be freely garbage collected. Every key of a WeakMap is an object. When the reference to this object is lost, the value can be garbage collected.

在地图对象中,永远不会垃圾收集项目。 相反,WeakMap允许自由地对其所有项目进行垃圾回收。 WeakMap的每个键都是一个对象。 当对该对象的引用丢失时,可以对该值进行垃圾回收。

Here are the main differences:

主要区别如下:

  1. you cannot iterate over the keys or values (or key-values) of a WeakMap

    您不能遍历WeakMap的键或值(或键值)
  2. you cannot clear all items from a WeakMap

    您无法从WeakMap清除所有项目
  3. you cannot check its size

    你不能检查它的大小

A WeakMap exposes those methods, which are equivalent to the Map ones:

WeakMap公开了那些方法,这些方法与Map的方法等效:

  • get(k)

    get(k)

  • set(k, v)

    set(k, v)

  • has(k)

    has(k)

  • delete(k)

    delete(k)

The use cases of a WeakMap are less evident than the ones of a Map, and you might never find the need for them, but essentially it can be used to build a memory-sensitive cache that is not going to interfere with garbage collection, or for careful encapsulation and information hiding.

WeakMap的用例不如Map的用例明显,您可能永远找不到它们的需要,但从本质上讲,它可以用于构建对内存敏感的缓存,而不会干扰垃圾回收,或者用于仔细的封装和信息隐藏。

发电机 (Generators)

Generators are a special kind of function with the ability to pause itself, and resume later, allowing other code to run in the meantime.

生成器是一种特殊的功能,具有暂停自身和稍后恢复的功能,允许同时运行其他代码。

See the full JavaScript Generators Guide for a detailed explanation of the topic.

有关该主题的详细说明,请参见完整JavaScript生成器指南。

The code decides that it has to wait, so it lets other code “in the queue” to run, and keeps the right to resume its operations “when the thing it’s waiting for” is done.

该代码决定必须等待,因此它允许“队列中”的其他代码运行,并保留“等待时”继续执行其操作的权利。

All this is done with a single, simple keyword: yield. When a generator contains that keyword, the execution is halted.

所有这些都是通过一个简单的关键字yield 。 当生成器包含该关键字时,将停止执行。

A generator can contain many yield keywords, thus halting itself multiple times, and it's identified by the *function keyword, which is not to be confused with the pointer dereference operator used in lower level programming languages such as C, C++ or Go.

生成器可以包含许多yield关键字,从而使自身多次停止,并由*function关键字标识,不要将它与在较低级编程语言(例如C,C ++或Go)中使用的指针取消引用运算符相混淆。

Generators enable whole new paradigms of programming in JavaScript, allowing:

Generators enable whole new paradigms of programming in JavaScript, allowing:

  • 2-way communication while a generator is running

    2-way communication while a generator is running
  • long-lived while loops which do not freeze your program

    long-lived while loops which do not freeze your program

Here is an example of a generator which explains how it all works.

Here is an example of a generator which explains how it all works.

function *calculator(input) {
    var doubleThat = 2 * (yield (input / 2))
    var another = yield (doubleThat)
    return (input * doubleThat * another)
}

We initialize it with

We initialize it with

const calc = calculator(10)

Then we start the iterator on our generator:

Then we start the iterator on our generator:

calc.next()

This first iteration starts the iterator. The code returns this object:

This first iteration starts the iterator. The code returns this object:

{
  done: false
  value: 5
}

What happens is: the code runs the function, with input = 10 as it was passed in the generator constructor. It runs until it reaches the yield, and returns the content of yield: input / 2 = 5. So we got a value of 5, and the indication that the iteration is not done (the function is just paused).

What happens is: the code runs the function, with input = 10 as it was passed in the generator constructor. It runs until it reaches the yield , and returns the content of yield : input / 2 = 5 . So we got a value of 5, and the indication that the iteration is not done (the function is just paused).

In the second iteration we pass the value 7:

In the second iteration we pass the value 7 :

calc.next(7)

and what we got back is:

and what we got back is:

{
  done: false
  value: 14
}

7 was placed as the value of doubleThat. Important: you might read like input / 2 was the argument, but that's just the return value of the first iteration. We now skip that, and use the new input value, 7, and multiply it by 2.

7 was placed as the value of doubleThat . Important: you might read like input / 2 was the argument, but that's just the return value of the first iteration. We now skip that, and use the new input value, 7 , and multiply it by 2.

We then reach the second yield, and that returns doubleThat, so the returned value is 14.

We then reach the second yield, and that returns doubleThat , so the returned value is 14 .

In the next, and last, iteration, we pass in 100

In the next, and last, iteration, we pass in 100

calc.next(100)

and in return we got

and in return we got

{
  done: true
  value: 14000
}

As the iteration is done (no more yield keywords found) and we just return (input * doubleThat * another) which amounts to 10 * 14 * 100.

As the iteration is done (no more yield keywords found) and we just return (input * doubleThat * another) which amounts to 10 * 14 * 100 .



Those were the features introduced in ES2015. Let’s now dive into ES2016 which is much smaller in scope.

Those were the features introduced in ES2015. Let's now dive into ES2016 which is much smaller in scope.



Array.prototype.includes() (Array.prototype.includes())

This feature introduces a more readable syntax for checking if an array contains an element.

This feature introduces a more readable syntax for checking if an array contains an element.

With ES6 and lower, to check if an array contained an element you had to use indexOf, which checks the index in the array, and returns -1 if the element is not there.

With ES6 and lower, to check if an array contained an element you had to use indexOf , which checks the index in the array, and returns -1 if the element is not there.

Since -1 is evaluated as a true value, you could not do for example

Since -1 is evaluated as a true value, you could not do for example

if (![1,2].indexOf(3)) {
  console.log('Not found')
}

With this feature introduced in ES7 we can do

With this feature introduced in ES7 we can do

if (![1,2].includes(3)) {
  console.log('Not found')
}

求幂运算符 (Exponentiation Operator)

The exponentiation operator ** is the equivalent of Math.pow(), but brought into the language instead of being a library function.

The exponentiation operator ** is the equivalent of Math.pow() , but brought into the language instead of being a library function.

Math.pow(4, 2) == 4 ** 2

This feature is a nice addition for math intensive JS applications.

This feature is a nice addition for math intensive JS applications.

The ** operator is standardized across many languages including Python, Ruby, MATLAB, Lua, Perl and many others.

The ** operator is standardized across many languages including Python, Ruby, MATLAB, Lua, Perl and many others.



Those were the features introduced in 2016. Let’s now dive into 2017

Those were the features introduced in 2016. Let's now dive into 2017



字符串填充 (String padding)

The purpose of string padding is to add characters to a string, so it reaches a specific length.

The purpose of string padding is to add characters to a string , so it reaches a specific length .

ES2017 introduces two String methods: padStart() and padEnd().

ES2017 introduces two String methods: padStart() and padEnd() .

padStart(targetLength [, padString])
padEnd(targetLength [, padString])

Sample usage:

Sample usage:

Object.values() (Object.values())

This method returns an array containing all the object own property values.

This method returns an array containing all the object own property values.

Usage:

用法:

const person = { name: 'Fred', age: 87 }
Object.values(person) // ['Fred', 87]

Object.values() also works with arrays:

Object.values() also works with arrays:

const people = ['Fred', 'Tony']
Object.values(people) // ['Fred', 'Tony']

Object.entries() (Object.entries())

This method returns an array containing all the object own properties, as an array of [key, value] pairs.

This method returns an array containing all the object own properties, as an array of [key, value] pairs.

Usage:

用法:

const person = { name: 'Fred', age: 87 }
Object.entries(person) // [['name', 'Fred'], ['age', 87]]

Object.entries() also works with arrays:

Object.entries() also works with arrays:

const people = ['Fred', 'Tony']Object.entries(people) // [['0', 'Fred'], ['1', 'Tony']]

Object.getOwnPropertyDescriptors() (Object.getOwnPropertyDescriptors())

This method returns all own (non-inherited) properties descriptors of an object.

This method returns all own (non-inherited) properties descriptors of an object.

Any object in JavaScript has a set of properties, and each of these properties has a descriptor.

Any object in JavaScript has a set of properties, and each of these properties has a descriptor.

A descriptor is a set of attributes of a property, and it’s composed by a subset of the following:

A descriptor is a set of attributes of a property, and it's composed by a subset of the following:

  • value: the value of the property

    value : the value of the property

  • writable: true the property can be changed

    writable : true the property can be changed

  • get: a getter function for the property, called when the property is read

    get : a getter function for the property, called when the property is read

  • set: a setter function for the property, called when the property is set to a value

    set : a setter function for the property, called when the property is set to a value

  • configurable: if false, the property cannot be removed nor any attribute can be changed, except its value

    configurable : if false, the property cannot be removed nor any attribute can be changed, except its value

  • enumerable: true if the property is enumerable

    enumerable : true if the property is enumerable

Object.getOwnPropertyDescriptors(obj) accepts an object, and returns an object with the set of descriptors.

Object.getOwnPropertyDescriptors(obj) accepts an object, and returns an object with the set of descriptors.

In what way is this useful? (In what way is this useful?)

ES6 gave us Object.assign(), which copies all enumerable own properties from one or more objects, and return a new object.

ES6 gave us Object.assign() , which copies all enumerable own properties from one or more objects, and return a new object.

However there is a problem with that, because it does not correctly copies properties with non-default attributes.

However there is a problem with that, because it does not correctly copies properties with non-default attributes.

If an object for example has just a setter, it’s not correctly copied to a new object, using Object.assign().

If an object for example has just a setter, it's not correctly copied to a new object, using Object.assign() .

For example with

For example with

const person1 = {
    set name(newName) {
        console.log(newName)
    }
}

This won’t work:

This won't work:

const person2 = {}
Object.assign(person2, person1)

But this will work:

But this will work:

const person3 = {}Object.defineProperties(person3,  Object.getOwnPropertyDescriptors(person1))

As you can see with a simple console test:

As you can see with a simple console test:

person1.name = 'x'
"x"

person2.name = 'x'

person3.name = 'x'
"x"

person2 misses the setter, it was not copied over.

person2 misses the setter, it was not copied over.

The same limitation goes for shallow cloning objects with Object.create().

The same limitation goes for shallow cloning objects with Object.create() .

Trailing commas (Trailing commas)

This feature allows to have trailing commas in function declarations, and in functions calls:

This feature allows to have trailing commas in function declarations, and in functions calls:

const doSomething = (var1, var2,) => {
  //...
}

doSomething('test2', 'test2',)

This change will encourage developers to stop the ugly “comma at the start of the line” habit.

This change will encourage developers to stop the ugly “comma at the start of the line” habit.

异步功能 (Async functions)

JavaScript evolved in a very short time from callbacks to promises (ES2015), and since ES2017 asynchronous JavaScript is even simpler with the async/await syntax.

JavaScript evolved in a very short time from callbacks to promises (ES2015), and since ES2017 asynchronous JavaScript is even simpler with the async/await syntax.

Async functions are a combination of promises and generators, and basically, they are a higher level abstraction over promises. Let me repeat: async/await is built on promises.

Async functions are a combination of promises and generators, and basically, they are a higher level abstraction over promises. Let me repeat: async/await is built on promises .

Why were async/await introduced? (Why were async/await introduced?)

They reduce the boilerplate around promises, and the “don’t break the chain” limitation of chaining promises.

They reduce the boilerplate around promises, and the “don't break the chain” limitation of chaining promises.

When Promises were introduced in ES2015, they were meant to solve a problem with asynchronous code, and they did, but over the 2 years that separated ES2015 and ES2017, it was clear that promises could not be the final solution.

When Promises were introduced in ES2015, they were meant to solve a problem with asynchronous code, and they did, but over the 2 years that separated ES2015 and ES2017, it was clear that promises could not be the final solution .

Promises were introduced to solve the famous callback hell problem, but they introduced complexity on their own, and syntax complexity.

Promises were introduced to solve the famous callback hell problem, but they introduced complexity on their own, and syntax complexity.

They were good primitives around which a better syntax could be exposed to developers, so when the time was right we got async functions.

They were good primitives around which a better syntax could be exposed to developers, so when the time was right we got async functions .

They make the code look like it’s synchronous, but it’s asynchronous and non-blocking behind the scenes.

They make the code look like it's synchronous, but it's asynchronous and non-blocking behind the scenes.

这个怎么运作 (How it works)

An async function returns a promise, like in this example:

An async function returns a promise, like in this example:

const doSomethingAsync = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve('I did something'), 3000)
  })
}

When you want to call this function you prepend await, and the calling code will stop until the promise is resolved or rejected. One caveat: the client function must be defined as async. Here's an example:

When you want to call this function you prepend await , and the calling code will stop until the promise is resolved or rejected . One caveat: the client function must be defined as async . 这是一个例子:

const doSomething = async () => {
  console.log(await doSomethingAsync())
}

A quick example (A quick example)

This is a simple example of async/await used to run a function asynchronously:

This is a simple example of async/await used to run a function asynchronously:

const doSomethingAsync = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve('I did something'), 3000)
  })
}

const doSomething = async () => {
  console.log(await doSomethingAsync())
}

console.log('Before')
doSomething()
console.log('After')

The above code will print the following to the browser console:

The above code will print the following to the browser console:

Before
After
I did something //after 3s

Promise all the things (Promise all the things)

Prepending the async keyword to any function means that the function will return a promise.

Prepending the async keyword to any function means that the function will return a promise.

Even if it’s not doing so explicitly, it will internally make it return a promise.

Even if it's not doing so explicitly, it will internally make it return a promise.

This is why this code is valid:

This is why this code is valid:

const aFunction = async () => {
  return 'test'
}

aFunction().then(alert) // This will alert 'test'

and it’s the same as:

and it's the same as:

const aFunction = async () => {
  return Promise.resolve('test')
}

aFunction().then(alert) // This will alert 'test'

The code is much simpler to read (The code is much simpler to read)

As you can see in the example above, our code looks very simple. Compare it to code using plain promises, with chaining and callback functions.

As you can see in the example above, our code looks very simple. Compare it to code using plain promises, with chaining and callback functions.

And this is a very simple example, the major benefits will arise when the code is much more complex.

And this is a very simple example, the major benefits will arise when the code is much more complex.

For example here’s how you would get a JSON resource, and parse it, using promises:

For example here's how you would get a JSON resource, and parse it, using promises:

const getFirstUserData = () => {
  return fetch('/users.json') // get users list
    .then(response => response.json()) // parse JSON
    .then(users => users[0]) // pick first user
    .then(user => fetch(`/users/${user.name}`)) // get user data
    .then(userResponse => response.json()) // parse JSON
}

getFirstUserData()

And here is the same functionality provided using await/async:

And here is the same functionality provided using await/async:

const getFirstUserData = async () => {
  const response = await fetch('/users.json') // get users list
  const users = await response.json() // parse JSON
  const user = users[0] // pick first user
  const userResponse = await fetch(`/users/${user.name}`) // get user data
  const userData = await user.json() // parse JSON
  return userData
}

getFirstUserData()

Multiple async functions in series (Multiple async functions in series)

Async functions can be chained very easily, and the syntax is much more readable than with plain promises:

Async functions can be chained very easily, and the syntax is much more readable than with plain promises:

const promiseToDoSomething = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve('I did something'), 10000)
  })
}

const watchOverSomeoneDoingSomething = async () => {
  const something = await promiseToDoSomething()
  return something + ' and I watched'
}

const watchOverSomeoneWatchingSomeoneDoingSomething = async () => {
  const something = await watchOverSomeoneDoingSomething()
  return something + ' and I watched as well'
}

watchOverSomeoneWatchingSomeoneDoingSomething().then(res => {
  console.log(res)
})

Will print:

Will print:

I did something and I watched and I watched as well

Easier debugging (Easier debugging)

Debugging promises is hard because the debugger will not step over asynchronous code.

Debugging promises is hard because the debugger will not step over asynchronous code.

Async/await makes this very easy because to the compiler it’s just like synchronous code.

Async/await makes this very easy because to the compiler it's just like synchronous code.

Shared Memory and Atomics (Shared Memory and Atomics)

WebWorkers are used to create multithreaded programs in the browser.

WebWorkers are used to create multithreaded programs in the browser.

They offer a messaging protocol via events. Since ES2017, you can create a shared memory array between web workers and their creator, using a SharedArrayBuffer.

They offer a messaging protocol via events. Since ES2017, you can create a shared memory array between web workers and their creator, using a SharedArrayBuffer .

Since it’s unknown how much time writing to a shared memory portion takes to propagate, Atomics are a way to enforce that when reading a value, any kind of writing operation is completed.

Since it's unknown how much time writing to a shared memory portion takes to propagate, Atomics are a way to enforce that when reading a value, any kind of writing operation is completed.

Any more detail on this can be found in the spec proposal, which has since been implemented.

Any more detail on this can be found in the spec proposal , which has since been implemented.



This was ES2017. Let me now introduce the ES2018 features

This was ES2017. Let me now introduce the ES2018 features



剩余/价差属性 (Rest/Spread Properties)

ES2015 introduced the concept of a rest element when working with array destructuring:

ES2015 introduced the concept of a rest element when working with array destructuring :

const numbers = [1, 2, 3, 4, 5]
[first, second, ...others] = numbers

and spread elements:

and spread elements :

const numbers = [1, 2, 3, 4, 5]
const sum = (a, b, c, d, e) => a + b + c + d + e
const sum = sum(...numbers)

ES2018 introduces the same but for objects.

ES2018 introduces the same but for objects.

Rest properties:

Rest properties :

const { first, second, ...others } = { first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }

first // 1
second // 2
others // { third: 3, fourth: 4, fifth: 5 }

Spread properties allow to create a new object by combining the properties of the object passed after the spread operator:

Spread properties allow to create a new object by combining the properties of the object passed after the spread operator:

const items = { first, second, ...others }
items //{ first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }

Asynchronous iteration (Asynchronous iteration)

The new construct for-await-of allows you to use an async iterable object as the loop iteration:

The new construct for-await-of allows you to use an async iterable object as the loop iteration:

for await (const line of readLines(filePath)) {
  console.log(line)
}

Since this uses await, you can use it only inside async functions, like a normal await.

Since this uses await , you can use it only inside async functions, like a normal await .

Promise.prototype.finally() (Promise.prototype.finally())

When a promise is fulfilled, successfully it calls the then() methods, one after another.

When a promise is fulfilled, successfully it calls the then() methods, one after another.

If something fails during this, the then() methods are jumped and the catch() method is executed.

If something fails during this, the then() methods are jumped and the catch() method is executed.

finally() allow you to run some code regardless of the successful or not successful execution of the promise:

finally() allow you to run some code regardless of the successful or not successful execution of the promise:

fetch('file.json')
  .then(data => data.json())
  .catch(error => console.error(error))
  .finally(() => console.log('finished'))

Regular Expression improvements (Regular Expression improvements)

ES2018 introduced a number of improvements regarding Regular Expressions. I recommend my tutorial on them, available at https://flaviocopes.com/javascript-regular-expressions/.

ES2018 introduced a number of improvements regarding Regular Expressions. I recommend my tutorial on them, available at https://flaviocopes.com/javascript-regular-expressions/ .

Here are the ES2018 specific additions.

Here are the ES2018 specific additions.

RegExp lookbehind assertions: match a string depending on what precedes it (RegExp lookbehind assertions: match a string depending on what precedes it)

This is a lookahead: you use ?= to match a string that's followed by a specific substring:

This is a lookahead: you use ?= to match a string that's followed by a specific substring:

/Roger(?=Waters)/

/Roger(?= Waters)/.test('Roger is my dog') //false
/Roger(?= Waters)/.test('Roger is my dog and Roger Waters is a famous musician') //true

?! performs the inverse operation, matching if a string is not followed by a specific substring:

?! performs the inverse operation, matching if a string is not followed by a specific substring:

/Roger(?!Waters)/

/Roger(?! Waters)/.test('Roger is my dog') //true
/Roger(?! Waters)/.test('Roger Waters is a famous musician') //false

Lookaheads use the ?= symbol. They were already available.

Lookaheads use the ?= symbol. They were already available.

Lookbehinds, a new feature, uses ?<=.

Lookbehinds , a new feature, uses ?<= .

/(?<=Roger) Waters/

/(?<=Roger) Waters/.test('Pink Waters is my dog') //false
/(?<=Roger) Waters/.test('Roger is my dog and Roger Waters is a famous musician') //true

A lookbehind is negated using ?<!:

A lookbehind is negated using ?<!

/(?<!Roger) Waters/

/(?<!Roger) Waters/.test('Pink Waters is my dog') //true
/(?<!Roger) Waters/.test('Roger is my dog and Roger Waters is a famous musician') //false

Unicode property escapes \p{…} and \P{…} (Unicode property escapes \p{…} and \P{…})

In a regular expression pattern you can use \d to match any digit, \s to match any character that's not a white space, \w to match any alphanumeric character, and so on.

In a regular expression pattern you can use \d to match any digit, \s to match any character that's not a white space, \w to match any alphanumeric character, and so on.

This new feature extends this concept to all Unicode characters introducing \p{} and is negation \P{}.

This new feature extends this concept to all Unicode characters introducing \p{} and is negation \P{} .

Any unicode character has a set of properties. For example Script determines the language family, ASCII is a boolean that's true for ASCII characters, and so on. You can put this property in the graph parentheses, and the regex will check for that to be true:

Any unicode character has a set of properties. For example Script determines the language family, ASCII is a boolean that's true for ASCII characters, and so on. You can put this property in the graph parentheses, and the regex will check for that to be true:

/^\p{ASCII}+$/u.test('abc')   //
/^\p{ASCII}+$/u.test('ABC@')  //
/^\p{ASCII}+$/u.test('ABC') //

ASCII_Hex_Digit is another boolean property, that checks if the string only contains valid hexadecimal digits:

ASCII_Hex_Digit is another boolean property, that checks if the string only contains valid hexadecimal digits:

/^\p{ASCII_Hex_Digit}+$/u.test('0123456789ABCDEF') //
/^\p{ASCII_Hex_Digit}+$/u.test('h')                //

There are many other boolean properties, which you just check by adding their name in the graph parentheses, including Uppercase, Lowercase, White_Space, Alphabetic, Emoji and more:

There are many other boolean properties, which you just check by adding their name in the graph parentheses, including Uppercase , Lowercase , White_Space , Alphabetic , Emoji and more:

/^\p{Lowercase}$/u.test('h') //
/^\p{Uppercase}$/u.test('H') //

/^\p{Emoji}+$/u.test('H')   //
/^\p{Emoji}+$/u.test('') //

In addition to those binary properties, you can check any of the unicode character properties to match a specific value. In this example, I check if the string is written in the greek or latin alphabet:

In addition to those binary properties, you can check any of the unicode character properties to match a specific value. In this example, I check if the string is written in the greek or latin alphabet:

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

Read more about all the properties you can use directly on the proposal.

Read more about all the properties you can use directly on the proposal .

Named capturing groups (Named capturing groups)

In ES2018 a capturing group can be assigned to a name, rather than just being assigned a slot in the result array:

In ES2018 a capturing group can be assigned to a name, rather than just being assigned a slot in the result array:

const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const result = re.exec('2015-01-02')

// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';

The s flag for regular expressions (The s flag for regular expressions)

The s flag, short for single line, causes the . to match new line characters as well. Without it, the dot matches regular characters but not the new line:

The s flag, short for single line , causes the . to match new line characters as well. Without it, the dot matches regular characters but not the new line:

/hi.welcome/.test('hi\nwelcome') // false
/hi.welcome/s.test('hi\nwelcome') // true


ESNext (ESNext)

What’s next? ESNext.

下一步是什么? ESNext.

ESNext is a name that always indicates the next version of JavaScript.

ESNext is a name that always indicates the next version of JavaScript.

The current ECMAScript version is ES2018. It was released in June 2018.

The current ECMAScript version is ES2018 . It was released in June 2018.

Historically JavaScript editions have been standardized during the summer, so we can expect ECMAScript 2019 to be released in summer 2019.

Historically JavaScript editions have been standardized during the summer, so we can expect ECMAScript 2019 to be released in summer 2019.

So at the time of writing, ES2018 has been released, and ESNext is ES2019

So at the time of writing, ES2018 has been released, and ESNext is ES2019

Proposals to the ECMAScript standard are organized in stages. Stages 1–3 are an incubator of new features, and features reaching Stage 4 are finalized as part of the new standard.

Proposals to the ECMAScript standard are organized in stages. Stages 1–3 are an incubator of new features, and features reaching Stage 4 are finalized as part of the new standard.

At the time of writing we have a number of features at Stage 4. I will introduce them in this section. The latest versions of the major browsers should already implement most of those.

At the time of writing we have a number of features at Stage 4 . I will introduce them in this section. The latest versions of the major browsers should already implement most of those.

Some of those changes are mostly for internal use, but it’s also good to know what is going on.

Some of those changes are mostly for internal use, but it's also good to know what is going on.

There are other features at Stage 3, which might be promoted to Stage 4 in the next few months, and you can check them out on this GitHub repository: https://github.com/tc39/proposals.

There are other features at Stage 3, which might be promoted to Stage 4 in the next few months, and you can check them out on this GitHub repository: https://github.com/tc39/proposals .

Array.prototype。{flat,flatMap} (Array.prototype.{flat,flatMap})

flat() is a new array instance method that can create a one-dimensional array from a multidimensional array.

flat() is a new array instance method that can create a one-dimensional array from a multidimensional array.

Example:

例:

['Dog', ['Sheep', 'Wolf']].flat()
//[ 'Dog', 'Sheep', 'Wolf' ]

By default it only “flats” up to one level, but you can add a parameter to set the number of levels you want to flat the array to. Set it to Infinity to have unlimited levels:

By default it only “flats” up to one level, but you can add a parameter to set the number of levels you want to flat the array to. Set it to Infinity to have unlimited levels:

['Dog', ['Sheep', ['Wolf']]].flat()
//[ 'Dog', 'Sheep', [ 'Wolf' ] ]

['Dog', ['Sheep', ['Wolf']]].flat(2)
//[ 'Dog', 'Sheep', 'Wolf' ]

['Dog', ['Sheep', ['Wolf']]].flat(Infinity)
//[ 'Dog', 'Sheep', 'Wolf' ]

If you are familiar with the JavaScript map() method of an array, you know that using it you can execute a function on every element of an array.

If you are familiar with the JavaScript map() method of an array, you know that using it you can execute a function on every element of an array.

flatMap() is a new Array instance method that combines flat() with map(). It's useful when calling a function that returns an array in the map() callback, but you want your resulted array to be flat:

flatMap() is a new Array instance method that combines flat() with map() . It's useful when calling a function that returns an array in the map() callback, but you want your resulted array to be flat:

['My dog', 'is awesome'].map(words => words.split(' '))
//[ [ 'My', 'dog' ], [ 'is', 'awesome' ] ]

['My dog', 'is awesome'].flatMap(words => words.split(' '))
//[ 'My', 'dog', 'is', 'awesome' ]

Optional catch binding (Optional catch binding)

Sometimes we don’t need to have a parameter bound to the catch block of a try/catch.

Sometimes we don't need to have a parameter bound to the catch block of a try/catch.

We previously had to do:

We previously had to do:

try {
  //...
} catch (e) {
  //handle error
}

Even if we never had to use e to analyze the error. We can now simply omit it:

Even if we never had to use e to analyze the error. We can now simply omit it:

try {
  //...
} catch {
  //handle error
}

Object.fromEntries() (Object.fromEntries())

Objects have an entries() method, since ES2017.

Objects have an entries() method, since ES2017.

It returns an array containing all the object own properties, as an array of [key, value] pairs:

It returns an array containing all the object own properties, as an array of [key, value] pairs:

const person = { name: 'Fred', age: 87 }
Object.entries(person) // [['name', 'Fred'], ['age', 87]]

ES2019 introduces a new Object.fromEntries() method, which can create a new object from such array of properties:

ES2019 introduces a new Object.fromEntries() method, which can create a new object from such array of properties:

const person = { name: 'Fred', age: 87 }
const entries = Object.entries(person)
const newPerson = Object.fromEntries(entries)

person !== newPerson //true

String.prototype.{trimStart,trimEnd} (String.prototype.{trimStart,trimEnd})

This feature has been part of v8/Chrome for almost a year now, and it’s going to be standardized in ES2019.

This feature has been part of v8/Chrome for almost a year now, and it's going to be standardized in ES2019.

trimStart() (trimStart())

Return a new string with removed white space from the start of the original string

Return a new string with removed white space from the start of the original string

'Testing'.trimStart() //'Testing'
' Testing'.trimStart() //'Testing'
' Testing '.trimStart() //'Testing '
'Testing'.trimStart() //'Testing'

trimEnd() (trimEnd())

Return a new string with removed white space from the end of the original string

Return a new string with removed white space from the end of the original string

'Testing'.trimEnd() //'Testing'
' Testing'.trimEnd() //' Testing'
' Testing '.trimEnd() //' Testing'
'Testing '.trimEnd() //'Testing'

Symbol.prototype.description (Symbol.prototype.description)

You can now retrieve the description of a symbol by accessing its description property instead of having to use the toString() method:

You can now retrieve the description of a symbol by accessing its description property instead of having to use the toString() method:

const testSymbol = Symbol('Test')
testSymbol.description // 'Test'

JSON improvements (JSON improvements)

Before this change, the line separator (\u2028) and paragraph separator (\u2029) symbols were not allowed in strings parsed as JSON.

Before this change, the line separator (\u2028) and paragraph separator (\u2029) symbols were not allowed in strings parsed as JSON.

Using JSON.parse(), those characters resulted in a SyntaxError but now they parse correctly, as defined by the JSON standard.

Using JSON.parse(), those characters resulted in a SyntaxError but now they parse correctly, as defined by the JSON standard.

Well-formed JSON.stringify() (Well-formed JSON.stringify())

Fixes the JSON.stringify() output when it processes surrogate UTF-8 code points (U+D800 to U+DFFF).

Fixes the JSON.stringify() output when it processes surrogate UTF-8 code points (U+D800 to U+DFFF).

Before this change calling JSON.stringify() would return a malformed Unicode character (a "�").

Before this change calling JSON.stringify() would return a malformed Unicode character (a " ").

Now those surrogate code points can be safely represented as strings using JSON.stringify(), and transformed back into their original representation using JSON.parse().

Now those surrogate code points can be safely represented as strings using JSON.stringify() , and transformed back into their original representation using JSON.parse() .

Function.prototype.toString() (Function.prototype.toString())

Functions have always had an instance method called toString() which return a string containing the function code.

Functions have always had an instance method called toString() which return a string containing the function code.

ES2019 introduced a change to the return value to avoid stripping comments and other characters like whitespace, exactly representing the function as it was defined.

ES2019 introduced a change to the return value to avoid stripping comments and other characters like whitespace, exactly representing the function as it was defined.

If previously we had

If previously we had

function /* this is bar */ bar () {}

The behavior was this:

The behavior was this:

bar.toString() //'function bar() {}

now the new behavior is:

now the new behavior is:

bar.toString(); // 'function /* this is bar */ bar () {}'


Wrapping up, I hope this article helped you catch up on some of the latest JavaScript additions, and the new features we’ll see in 2019.

Wrapping up, I hope this article helped you catch up on some of the latest JavaScript additions, and the new features we'll see in 2019.

Click here to get a PDF / ePub / Mobi version of this post to read offline

Click here to get a PDF / ePub / Mobi version of this post to read offline

Flavio

弗拉维奥

翻译自: https://www.freecodecamp.org/news/es5-to-esnext-heres-every-feature-added-to-javascript-since-2015-d0c255e13c6e/

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/cumian8165/article/details/108107711

智能推荐

MPEG-2 详解-程序员宅基地

文章浏览阅读930次,点赞14次,收藏27次。MPEG-2 详解

对于G2LGAN中使用visualizaton引用 PyQt4.QtGui出错_g2—gan-程序员宅基地

文章浏览阅读83次。在ubuntu中想使用pyqt,follow了G2LGAN中的方法,还是不行,最后网上查到了方法,这里记录一下:sudo apt-get install python-qt4使用这个命令可以解决问题,需要注意的是还要参考我们的整体的环境,例如python的版本等..._g2—gan

c++面向对象程序设计教程(第四版)_c++面向对象程序设计教程(第4版) 陈维兴 pdf电子版第八章-程序员宅基地

文章浏览阅读1.4k次。c++面向对象程序设计教程(第四版)陈维兴 林小茶 编著 学习笔记_c++面向对象程序设计教程(第4版) 陈维兴 pdf电子版第八章

简单的个人网址导航设计页面模板-程序员宅基地

文章浏览阅读4.5k次。转载于:https://www.cnblogs.com/wordblog/p/6790901.html_个人导航页html模板

JAVA电视设备租借系统计算机毕业设计Mybatis+系统+数据库+调试部署-程序员宅基地

文章浏览阅读441次。JAVA电视设备租借系统计算机毕业设计Mybatis+系统+数据库+调试部署。springboot基于SpringBoot的在线古玩市场系统的设计与实现。springboot基于Springboot技术的装潢公司网站开发。springboot基于springboot的球队管理系统。jsp基于JSP的天津城建大学计算机学院校友录管理系统。JSP驾照管理系统设计(论文+系统+开题报告)JSP网上视频点播系统的设计与实现mysql。ssm基于B_S景区票务管理系统设计与实现。

NISP含金量?NISP真的有必要考么?NISP好考吗?NISP二级为什么那么贵?_nisp二级证书含金量-程序员宅基地

文章浏览阅读1.4k次。大学期间若报考NISP二级证书,持NISP本科满2年专科满4年可免试更换 CISP证书,NISP二级证书要在NISP一级网络信息安全基础理论理论知识管理体系认证,从事网络信息安全职位的工作人员需具备的国家证书。学生就业通行卡助推高薪就业。NISP证书简述 NISP证书三个级别,分别是:一级、二级、三级(专项) 证书。这个需要看可地区政策,政策不同奖励不同 NISP【资格证书优点】 NISP一级证书应该是各个行业工作人员信息安全意识普及化和网络信息安全基础培训的国家承认,是各岗位人员的 最基本资格证书。_nisp二级证书含金量

随便推点

建立干系人间有效沟通机制 6大重点_为什么要与干系人共享信息-程序员宅基地

文章浏览阅读847次,点赞22次,收藏20次。建立干系人间有效沟通机制 6大重点,CoCode看板和代码仓库助力项目高效沟通协作。_为什么要与干系人共享信息

注释语句(带源码)_codewg-程序员宅基地

文章浏览阅读99次。在编写代码时,程序员通常会添加注释来简化代码,并对代码进行解释说明。Python支持两种类型的注释,单行注释和多行注释。编写代码时,程序员应该尽可能多地添加注释,为自己和他人提供更好的阅读体验。这样,从井号开始,直到这行结束为止的所有内容都是注释。在需要添加注释的位置,可以在代码前面加上一个井号。下面给出一个完整的示例代码,演示了如何使用单行注释和多行注释。选定需要添加注释的代码块,然后在代码块前面添加三个双引号。选定需要添加注释的代码块,然后在代码块前面添加一个井号。代码块中的所有内容都会被视为注释。_codewg

Java黑皮书编程练习题6.05(对三个数排序)_使用下面的方法头编写方法,按升序显示三个数: public static void displays-程序员宅基地

文章浏览阅读657次,点赞2次,收藏2次。使用下面的方法头编写方法,按升序显示三个数:publicstaticvoid displaySortedNumbers(double num1, double num2, double num3)编写程序,提示用户输入3个数字,调用方法以升序显示它们import java.util.Scanner;public class Exercise06_05 { public static void displaySortedNumbers(double num1, double num2.._使用下面的方法头编写方法,按升序显示三个数: public static void displaysortedn

蓝队工具【IP地址白名单过滤器且微步查询】_blueteamtools-程序员宅基地

文章浏览阅读638次,点赞2次,收藏2次。f.ContentText.Text = string.Join("\r\n", list) + "\r\n不是有效IP格式";尽管我们在研判流量威胁时,已经确认了这些IP地址确实存在攻击流量,但是在防火墙封锁恶意IP地址的时候,经常要手工剔除这些IP,十分繁琐。f.ContentText.Text = "共删除" + list.Count + "项";list.Add(WBIplist[i].Replace("恶意"," "));r.msg = "微步查询信誉:" + r.msg;a += " 恶意";_blueteamtools

多齿配体DOTA-SH双功能螯合剂DOTA-巯基-程序员宅基地

文章浏览阅读352次,点赞11次,收藏8次。同时,DOTA-SH双功能螯合剂还可以通过连接靶向分子,实现放射性核素靶向治疗的目的。DOTA-SH双功能螯合剂,也称为多齿配体,是一种具有多个配位基团的有机分子,可以与金属离子形成稳定的络合物。由于其独特的结构和性质,DOTA-SH双功能螯合剂在许多领域都有广泛的应用,例如核医学、放射化学、生物医学工程等。DOTA-SH双功能螯合剂可用于制备金属放射性标记化合物,例如64Cu、67Ga、68Ga等。化学名称:1,4,7,10-四氮杂环十二烷-1,4,7-三(乙酸)-10-(2-硫乙基)乙酰胺。

深信服:借助观测云实现全链路可观测性-程序员宅基地

文章浏览阅读890次,点赞10次,收藏10次。观测云提供全链路可观测的能力,可以将前端、网关、Nginx、后端服务、中间件等整条链路的信息通过一个 trace_id 全部串起来,这样做的好处是不管哪里出现故障可以快速定位到具体的服务或中间件。