• type: FrontEnd
  • Title: ES5 to ESNext — Here’s every feature added to JavaScript since 2015
  • Link: medium.freecodecamp.org/es5-to-esne…
  • author: Flavio Copes

ES5 to ESNext — All the new features added to JavaScript since 2015

The purpose of this article is to help front-end developers connect with JavaScript before and after ES6, and to quickly understand the latest developments in the JavaScript language.

JavaScript is currently in a privileged position because it is the only language that runs in the browser and is highly integrated and optimized.

JavaScript has a great future, and keeping up with it won’t be any harder than it is now. My goal is to give you a quick and comprehensive overview of what’s new and available in the language.

Click here for PDF/ePub/Mobi version

directory

ECMAScript profile

ES2015

  • Let and const
  • Arrow function
  • class
  • The default parameters
  • Template string
  • Deconstruction assignment
  • Enhanced object literals
  • For – loops
  • Promises
  • The module
  • A String of new methods
  • New method Object
  • Expansion operator
  • Set
  • Map
  • Generators

ES2016

  • Array.prototype.includes()
  • Exponentiation operator

ES2017

  • String padding
  • Object.values()
  • Object.entries()
  • Object.getOwnPropertyDescriptors()
  • The tail comma
  • Shared memory and atomic operations

ES2018

  • Rest/Spread Properties
  • Asynchronous iteration
  • Promise.prototype.finally()
  • Regular expression improvement

ESNext

  • Array.prototype.{flat,flatMap}
  • Try /catch Optional parameter binding
  • Object.fromEntries()
  • String.prototype.{trimStart,trimEnd}
  • Symbol.prototype.description
  • JSON improvements
  • Well-formed JSON.stringify()
  • Function.prototype.toString()

ECMAScript profile

Whenever I read javascript-related articles, I often come across terms like this: ES3, ES5, ES6, ES7, ES8, ES2015, ES2016, ES2017, ECMAScript 2017, ECMAScript 2016, ECMAScript 2015, etc.

They all refer to a standard called ECMAScript.

JavaScript is implemented based on this standard, and ECMAScript is often abbreviated to ES.

In addition to JavaScript, other ecMAScript-based implementation languages include:

  • ActionScript(Flash scripting language), which is losing popularity as Adobe discontinues support for Flash at the end of 2020.
  • JScriptAt the height of the First Browser War, JavaScript was only supported by Netscape and Microsoft had to build their own scripting language for Internet Explorer.

But the most widely circulated and influential language implementation based on the ES standard is undoubtedly JavaScript

Why do you use such a strange name? Ecma International is the Swiss standards Association responsible for setting International standards.

After JavaScript was created, it was submitted to the European Computer Manufacturers Association for standardization by Netscape and Sun Microsystems and adopted as ECMA-262 under the name 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.

After IE9, references to the JScript name were removed from Microsoft’s browsers, which were instead referred to as JavaScript.

As a result, aS of 201x, JavaScript is the most popular language implemented based on the ECMAScript specification.

The current version of ECMAScript.

The latest version of ECMAScript is ES2018.

Released in June 2018.

What is TC39?

Technical Committee 39 (TC39) is a Committee that promotes the development of JavaScript.

TC39’s membership includes major browser vendors and companies whose businesses are closely related to browsers, including Mozilla, Google, Facebook, Apple, Microsoft, Intel, PayPal, SalesForce, and others.

Each standard version proposal must go through four different stages, which are explained in detail here.

ES Versions

What puzzles me is that the ES version is sometimes named after the iteration version number, but sometimes after the year. And the uncertainty of the naming makes it easier for people to confuse the two concepts of JS/ES 😄.

Prior to ES2015, naming conventions for various versions of ECMAScript were generally consistent with version updates that followed the standard. As a result, the official version of the 2009 ECMAScript specification update is ES5.

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. Why is all this happening? During the creation of ES2015, the name was changed from ES6 to ES2015, but since it was finalized too late, people still called it ES6, and the community didn’t leave the version number behind completely — the world still uses ES to define the version number.

The following figure clearly shows the relationship between version numbers and years:

Next, let’s take a closer look at the JavaScript features that have been added since ES5.

Let and const

Before ES2015, var was the only statement that could be used to declare variables.

var a = 0
Copy the code

If you omit var, you will assign the value (0) to an undeclared variable. There is some difference between declared and undeclared variables.

In modern browsers with strict mode enabled, assigning an undeclared variable raises a ReferenceError. In older browsers (or with strict mode disabled), an undeclared variable will implicitly become a property of a global object when performing an assignment.

When you declare a variable but do not initialize it, its value is undefined until you assign it.

var a //typeof a === 'undefined'
Copy the code

You can redeclare a variable multiple times and override it:

var a = 1
var a = 2
Copy the code

You can also declare multiple variables at once in a single declaration:

var a = 1, b = 2
Copy the code

A scope is the part of your code that a variable can access.

Variables declared with var outside of functions are assigned to global objects, which can be accessed in the global scope. Variables declared inside a function can only be accessed in the function’s local scope, similar to function parameters.

If the name of a local variable defined in a function is the same as the name of a global variable, then the local variable has higher priority and the global variable with the same name cannot be accessed in the function.

Note that var has no block-level scope (the identifier is a pair of curly braces), but var has a function scope, so declaring a variable in a newly created block-level scope or function scope overwrites a global variable with the same name, because var does not create a new scope in either case.

Inside a function, any variables defined in it are visible in all function code, because JavaScript actually moves all variables to the top level (something called hanging) before executing the code. Variables defined inside a function are visible (accessible) throughout the function scope, even if they are declared at the end of the function body, but can still be referenced at the beginning of the function body because of JavaScript’s variable promotion mechanism. To avoid confusion, declare variables at the beginning of a function to develop good coding practices.

Using let

Let, a new feature introduced in ES2015, is essentially a VAR with block-level scope. It can be accessed by the current scope (function and block-level scope) as well as by its child scope.

Modern JavaScript developers may prefer let over VAR.

If let seems like an abstract term, it makes sense when you read let color = ‘red’ because the use of let defines color as red.

Using let to declare variables outside of any function, as opposed to var, does not create global variables.

Using const

Variables declared using var or let variables can be reassigned. Once a variable declared with const is initialized, its value can never be changed; that is, it cannot be reassigned.

const a = 'test'
Copy the code

We can’t assign to a anymore. However, if a is an object that has properties or methods, then we can change its properties or methods.

Const does not imply immutability, only that the reference address of a variable declared with const is not changed.

Like let, const also has block-level scope.

Modern JavaScript developers should use const whenever possible when encountering variable declarations that do not perform secondary assignment.

Arrow function

The introduction of arrow functions has greatly changed the writing style and some of the working mechanisms of code.

In my opinion, arrow functions are very popular with developers, and the function keyword is now rarely seen in the newer code base, although it is not obsolete.

The arrow function looks much cleaner because it allows you to write functions in shorter syntax:

const myFunction = function() {
  / /...
}
Copy the code

to

const myFunction = (a)= > {
  / /...
}
Copy the code

If the function body contains only one statement, you can even omit the braces and write the statement directly:

const myFunction = (a)= > doSomething()
Copy the code

Arguments are passed in parentheses:

const myFunction = (param1, param2) = > doSomething(param1, param2)
Copy the code

If the function has only one argument, the parentheses can be omitted:

const myFunction = param= > doSomething(param)
Copy the code

This short syntax makes it easier to use shorter functions

Implicit return

Arrow functions support implicit return: you can return a value without using the return keyword.

An implicit return only works if the function body contains only one statement:

const myFunction = (a)= > 'test'
myFunction() //'test'
Copy the code

One case to be aware of is when an object is returned, enclose braces in brackets to avoid ambiguity and parse it into braces in the body of the function.

const myFunction = (a)= > ({ value: 'test' })
myFunction() //{value: 'test'}
Copy the code

In the arrow functionthis

This can be a difficult concept to master, as it varies from context to context and can behave differently in different JavaScript modes (strict mode or not).

Understanding the concept of this is important for using arrow functions because they behave very differently than regular functions.

When the method of the object is a regular function, the this in the method refers to the object, so we can do this:

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

Executing car.fullname () returns “Ford Fiesta”.

If the above method uses an arrow function, since the scope of this in the arrow inherits from the execution context, the arrow function itself is not bound to this, so the value of this will be looked up in the call stack, so the code car.fullname () does not return the same result as the normal function. Will actually return the string “undefined “:

const car = {
  model: 'Fiesta'.manufacturer: 'Ford'.fullName: (a)= > {
    return `The ${this.manufacturer} The ${this.model}`}}Copy the code

Therefore, arrow functions are not suitable as object methods.

Similarly, arrow functions are not suitable for use as creation constructors because TypeError is thrown when the object is instantiated.

So use regular functions when you don’t need dynamic context.

Of course, there are problems with using arrow functions on event listeners. Because DOM event listeners automatically bind this to the target element, if the event handler’s logic depends on this, the normal function is needed:

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

Classes class

JavaScript implements inheritance in a rare way: archetypal inheritance. While archetypal inheritance is great in my opinion, it differs from the inheritance implementation mechanism of most other popular programming languages, which is class-based.

The ECMAScript committee decided to implement class syntactic sugar on top of prototype inheritance to make JavaScript code easier to understand for developers of other languages that implement class-based inheritance.

Note: Class doesn’t change JavaScript at the bottom, and you can still access object prototypes directly.

The class definition

Here is an example of a class:

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

Class has an identifier, and we can use new ClassIdentifier() to create an object instance.

When the object is initialized, the constructor method is called and arguments are passed to it.

Class declaration statements can also add some of the stereotype methods required by the class. In this case, Hello is a prototype method of the Person class that can be called on an object instance of the class:

const flavio = new Person('Flavio')
flavio.hello()
Copy the code

The Class inheritance

A subclass can extend another class, and objects instantiated by subclasses can extend all methods of both classes.

If a method in a subclass has the same name as a method in its parent class, then a method in a subclass with the same name takes precedence:

class Programmer extends Person {
  hello() {
    return super.hello() + ' I am a programmer.'}}const flavio = new Programmer('Flavio')
flavio.hello()
Copy the code

(The above code prints: “Hello, I am Flavio. I am a Programmer.”)

Class does not display class variable declarations, but you must initialize class member variables in the initialization constructor.

In subclasses, you can refer to the parent class by calling super().

A static method

In a class, methods are usually mounted directly to the instance object and invoked directly on the instance object.

Static methods are called directly from the class name, not from the object instance:

class Person {
  static genericHello() {
    return 'Hello'
  }
}
Person.genericHello() //Hello
Copy the code

Private methods

JavaScript has no truly protected private methods built in.

The community has solutions, but I’m not going to explain them here.

Getters and setters

You can create getters and setters by prefixing methods get or set. Getters and setters will execute methods in get or set when you go to get or modify a particular value.

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

If you only have a getter, the property cannot be set, and any attempts to set it are ignored:

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

If you have only one setter, you can change the value, but you can’t access it externally:

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

The default parameters

The function doSomething takes a param1 argument.

const doSomething = (param1) = >{}Copy the code

We can set a default value for param1, which is automatically set to default if no parameter is passed when the function is called.

const doSomething = (param1 = 'test') = >{}Copy the code

Of course, this mechanism also works for multiple parameters:

const doSomething = (param1 = 'test', param2 = 'test2') = >{}Copy the code

What if your function is an object with a particular property?

Once upon a time, if we had to take the value of a particular property of an object, you would have to add some code to the function for compatibility processing (the object is not formatted correctly) :

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

By deconstructing, you can provide default values for specific attributes, which greatly simplifies code:

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

If no object is passed when we call colorize, we can also get a default object as an argument to use:

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

Template string

Template strings are different from previous versions of ES5, and you can use strings in novel ways.

This syntax seems very simple, just replace single or double quotes with a single backquote:

const a_string = `something`
Copy the code

This usage is unique in that it provides many functions that ordinary strings do not, such as:

  • It provides a good syntax for defining multi-line strings
  • It provides an easy way to insert variables and expressions into a string
  • It allows you to create DSLS with template tags (DSLS mean domain-specific languages, for example, as styled components define your component’s CSS in React)

Let’s dive into the details of each feature.

Multiline string

Prior to the ES6 standard, creating a string spanning two lines could only use the ” character at the end of the line:

const string =
  'first part \
second part'
Copy the code

This allows you to create strings that span two lines but still render as one line:

first part second part
Copy the code

To render multiple lines, add ‘\n’ at the end of the line, as in:

const string =
  'first line\n \
second line'
Copy the code

or

const string = 'first line\n' + 'second line'
Copy the code

Template strings make it easier to define multi-line strings.

A template string starts with a backquote, you just press Enter to create a new line, no special symbols need to be inserted, and the final rendering looks like this:

const string = `Hey this string is awesome! `
Copy the code

Note that Spaces have special meaning here if you do:

const string = `First Second`
Copy the code

It creates a string like this:

First
                Second
Copy the code

There is a simple way to fix this, just make the first line empty and then call a trim() method after adding the translation on the right to remove all Spaces before the first character:

const string = `
First
Second`.trim()
Copy the code

The interpolation

Template strings provide a convenient way to insert variables and expressions

You just need to use ${… } syntax

const var = 'test'
const string = `something The ${var}` //something test
Copy the code

You can add anything to ${}, even expressions:

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

Template tags

Tag templates may not sound like a very useful feature, but they are actually used by many popular libraries, such as Styled Components, Apollo, and the GraphQL client/server library, so it is crucial to understand how it works.

Used in the Styled Components template tag to define the CSS string

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

In Apollo, the template tag is used to define the GraphQL query schema:

const query = gql` query { ... } `
Copy the code

Styled. Button and the GQL template tag in the two examples above are functions:

function gql(literals, ... expressions) {}
Copy the code

This function returns a string, which can be of any type.

Literals are sequences of template literals that contain expression interpolation. Expressions contain all interpolation.

Here’s an example:

const string = `something The ${1 + 2 + 3}`
Copy the code

The literal in this example is a sequence of two parts. Part 1 is something, which is the string before the first interpolation position (${}), and part 2 is an empty string from the first interpolation position to the end of the string.

The expression in this example is a sequence of only one part, which is 6.

To take a more complicated example:

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

The first part of the sequence of literals in this example is:

;`something
another `
Copy the code

Part 2 is:

;`
new line `
Copy the code

Part 3 is:

;`
test`
Copy the code

The expression in this example contains two parts: x and 6.

The function that takes these values can do whatever it wants with them, and that’s the power of this feature.

For example, the simplest is string interpolation, concatenating literals with expressions:

const interpolated = interpolate`I paid The ${10}Euro `
Copy the code

The process of interpolation is:

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

Deconstruction assignment

Given an object, you can extract some of its values and assign them to the named variable:

const person = {
  firstName: 'Tom'.lastName: 'Cruise'.actor: true.age: 54.//made up
}
const {firstName: name, age} = person
Copy the code

Name and age contain corresponding values.

The same syntax applies to arrays:

const a = [1.2.3.4.5]
const [first, second] = a
Copy the code

This statement creates three new variables that take the values of the 0, 1, and 4 indices of array A:

const [first, second, , , fifth] = a
Copy the code

More powerful object literals

ES2015 gives object literals more power.

Simplified syntax for including variables

Originally written:

const something = 'y'
const x = {
  something: something
}
Copy the code

New writing:

const something = 'y'
const x = {
  something
}
Copy the code

The prototype

Stereotypes can be specified as follows:

const anObject = { y: 'y' }
const x = {
  __proto__: anObject
}
Copy the code

super()

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

Dynamic properties

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

For – loops

ES5 2009 introduced the forEach() loop, which is useful, but unlike the for loop, can’t be broken.

ES2015 introduces the **for-of** loop, which is a break function on the basis 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
}
Copy the code

Notice the use of const. The loop creates a new scope with each iteration, so we can use const instead of let.

It goes with the for… The difference between in and out is:

  • for... of Iterate over property values
  • for... in Iterate over the property name

Promises

The general definition of a promise is that it is a proxy through which you can eventually get a value.

Promises are a way to handle asynchronous code and write fewer callbacks.

Asynchronous functions are built on top of the Promise API, so understanding promises is a basic requirement.

A brief introduction to the principle of Promise

When a promise is called, it is first in the pending state. During the processing of a promise, the called function (caller) can continue to execute until the promise gives feedback.

At this point, the called function waits for the Promise result to be either resolved or Rejected. But because JavaScript is asynchronous, the function continues to execute during promise processing.

Why does JS API use Promises?

In addition to your code and third-party library code, Promise is used in modern Web apis, such as:

  • Battery API
  • Fetch API
  • Service Workers

In modern JavaScript, it’s impossible not to use promises, so let’s take a closer look at promises.

Create a Promise

The Promise API exposes a Promise constructor that can be initialized with 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)
  }
})
Copy the code

The Promise checks the done global variable and returns a Resolved Promise if it is true, and a Rejected Promise if it is not.

Resolve and Reject give us a return value, which can be either a string or an object.

Use a Promise

Here’s how to create a promise. Here’s how to consume it.

const isItDoneYet = new Promise(a)/ /...
const checkIfItsDone = (a)= > {
  isItDoneYet
    .then(ok= > {
      console.log(ok)
    })
    .catch(err= > {
      console.error(err)
    })
}
Copy the code

The checkIfItsDone() method executes the isItDoneYet() promise and waits for it to resolve using the then callback and, if there is an error, a catch callback.

Chain promise

A promise can return another promise to create a promise chain.

A good example of this is the Fetch API, which is a superlayer API based on the XMLHttpRequest API that you can use to Fetch resources and execute a series of chained promises when the resources are fetched.

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

The example of chained 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)
  })
Copy the code

In this example, we call fetch(), get a list of TODO items from the todos.json file in the root directory, and create a chained promise.

Running the fetch() method returns a response containing many attributes, from which we refer to the following:

  • status, a value representing the HTTP status code
  • statusText, a status message that is returned when the request is successfulOK

Response also has a JSON () method, which returns a promise that returns the result of converting the content to JSON.

So the invocation of these promises is: The first promise executes a status() method that we define, checks response Status, determines if it is a successful response (status between 200 and 299), and rejects the promise if it is not.

The reject operation causes the entire chained promise to skip all subsequent promises and go directly to the catch() statement, printing Request failed and error messages.

If the promise is successful, it calls the JSON () function we defined. Because the response object returned after the previous promise succeeded, we can take it and pass it in as an argument to the second promise.

In this example, we return JSON serialized data, so the third promise accepts the JSON directly:

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

Then we print it to console.

Handling errors

In the example in the previous section, we had a catch followed by a chained promise.

When either of the promise chains is wrong or reject, it jumps directly to the nearest catch() statement following the promise chain.

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)
})
Copy the code

Cascading error

If you throw an error inside a catch(), you can follow it up with a second catch() to handle the error, and so on.

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

Organize multiple Promises

Promise.all()

If you want to fulfill different promises at the same time, declare a series of promises with promise.all (), and then perform some actions when they are all resolved.

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)
  })
Copy the code

Using ES2015’s deconstructing assignment syntax, you can write:

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

This is not limited to using fetch, of course; this applies to any promise.

Promise.race()

Promise.race() runs all promises passed in, but as soon as one of the resolve promises is resolved, the callback is run, and only once. The argument to the callback is the result of the first resolve Promise.

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'
})
Copy the code

The module

ES Module is the ECMAScript standard for handling modules.

While Node.js has been using the CommonJS standard for years, browsers have never had a modular system, because decisions about modular systems need to be implemented first by browser vendors after ECMAScript standardization.

This standardization has been done in ES2015, and browsers are starting to implement it, and everyone is trying to be consistent and work the same way. ES Module is now available in Chrome Safari Edge and Firefox (starting with version 60).

Modules are cool because they allow you to encapsulate various functions and expose those functions as libraries to other JavaScript files.

ES module syntax

Introduce the syntax of the module:

import package from 'module-name'
Copy the code

CommonJS is used like this:

const package = require('module-name')
Copy the code

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

uppercase.js

export default str => str.toUpperCase()
Copy the code

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

Now, any other JavaScript module can import upperCase.js via import.

An HTML page can add a module by using the

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

Note: The module import behaves just like the *defer* script load. Read read load JavaScript with defer and async

It is important to note that any scripts loaded with type=”module” will be loaded in strict mode.

In this case, the uppercase.js module defines a default export, so when we import it we can give it any name we like:

import toUpperCase from './uppercase.js'
Copy the code

And we can use it like this:

toUpperCase('test') //'TEST'
Copy the code

You can also import modules via an absolute path. Here is an example of a reference to a module defined under another field:

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

Here are also some valid import syntax:

import { toUpperCase } from '/uppercase.js'
import { toUpperCase } from '.. /uppercase.js'
Copy the code

The following are incorrect uses:

import { toUpperCase } from 'uppercase.js'
import { toUpperCase } from 'utils/uppercase.js'
Copy the code

Because we’re not using absolute addresses or relative addresses.

Additional import/export syntax

We see the above example:

export default str => str.toUpperCase()
Copy the code

A default export is generated here. However, you can export multiple functions in a single file using the following syntax:

const a = 1
const b = 2
const c = 3
export { a, b, c }
Copy the code

Another module can import all using the following method:

import * from 'module'
Copy the code

You can also import only parts of the assignment by deconstructing it:

import { a } from 'module'
import { a, b } from 'module'
Copy the code

For convenience, you can also rename anything that is imported using as:

import { a, b as two } from 'module'
Copy the code

You can import the default exit in the module as well as any non-default exit by name:

import React, { Component } from 'react'
Copy the code

This is an article about the ES module, which can be seen at glitch.com/edit/#! / fla…

CORS(Cross-domain Resource Sharing)

The remote acquisition of modules follows the CORS mechanism. This means that when you reference remote modules, you must use valid CORS headers to Allow cross-domain Access (e.g. Access-control-allow-origin: *).

What should I do about browsers that don’t support modules?

Use this with type=”module” and nomodule:

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

Packing module

The ES module is a feature of modern browsers. These features are part of the ES6 specification and are a long way from being fully implemented in the browser.

We can use them now! But we also need to know that there are some modules that can have a performance impact on the performance of our pages. Because the browser has to execute them at runtime.

Webpack will probably still be heavily used, even though the ES module can be executed in a browser. But this feature built into the language is a great unification between clients and NodeJS when using modules.

New string methods

Any string has some instance methods:

  • repeat()
  • codePointAt()

repeat()

Repeat the string as many times as specified:

'Ho'.repeat(3) //'HoHoHo'
Copy the code

Returns an empty string if no argument is provided and 0 is used as an argument. If you give a negative parameter, you get a RangeError.

codePointAt()

This method can be used to handle characters that require two UTF-16 unit representations.

To use charCodeAt, you need to get two utF-16 codes separately and combine them. But with codePointAt() you can get the entire character directly.

Here is an example, the Chinese “𠮷” is a combination of two UTF-16 encodings:

"𠮷".charCodeAt(0).toString(16) //d842
"𠮷".charCodeAt(1).toString(16) //dfb7
Copy the code

If you combine two Unicode characters:

"\ud842\udfb7" / / "𠮷"
Copy the code

You can also get the same result with codePointAt() :

"𠮷".codePointAt(0) //20bb7
Copy the code

If you combine the resulting Unicode encodings:

"\u{20bb7}" / / "𠮷"
Copy the code

For more information on how to use Unicode, see my Unicode Guide.

New object methods

ES2015 introduces some static methods under the Object class:

  • Object.is()Determine if the two values are the same
  • Object.assign()Used to make a shallow copy of an object
  • Object.setPrototypeOfSets the prototype of an object

Object.is()

This method is used to help compare the values of objects:

Usage:

Object.is(a, b)
Copy the code

The return value is always false except for:

  • abIt’s the same object
  • abAre equal strings (strings combined with the same characters are equal)
  • abIt’s the same number
  • abAre allundefined.null.NaN.trueOr arefalse

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

Object.assign()

Introduced in the ES2015 version, this method copies all enumerable self properties from a given object into another object.

The basic use of this API is to create a shallow copy of an object.

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

As a shallow copy, the value is copied, and the object copies its reference (not the object itself), so when you change an attribute value of the source object, the change also takes effect in the copied object, because the internal reference 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
Copy the code

As I mentioned earlier, source objects can be one or more:

const wisePerson = {
  isWise: true
}
const foolishPerson = {
  isFoolish: true
}
const wiseAndFoolishPerson = Object.assign({}, wisePerson, foolishPerson)
console.log(wiseAndFoolishPerson) //{ isWise: true, isFoolish: true }
Copy the code

Object.setPrototypeOf()

Sets the prototype of an object. You can accept two parameters: the object and the prototype.

Usage:

Object.setPrototypeOf(object, prototype)
Copy the code

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
Copy the code

Expansion operator

You can expand an array, an object, or even a string by using the expansion operator… .

Let’s take an array as an example and give:

const a = [1.2.3]
Copy the code

You can create a new array as follows:

const b = [...a, 4.5.6]
Copy the code

You can also create a copy of the array as follows:

const c = [...a]
Copy the code

This approach is still valid for objects. Clone an object as follows:

constnewObj = { ... oldObj }Copy the code

When used on strings, the expansion operator creates an array of each character in the string:

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

This operator has some very useful applications. One of the most important is the ability to use arrays as function arguments in a very simple way:

const f = (foo, bar) = > {}
const a = [1.2] f(... a)Copy the code

(In the previous syntax specification, you could only do this with f.ply (null, a), which was not very friendly or readable.)

Residual arguments (rest elements) are useful when used in conjunction with array destructuring.

const numbers = [1.2.3.4.5]
[first, second, ...others] = numbers
Copy the code

Here are spread elements:

const numbers = [1.2.3.4.5]
const sum = (a, b, c, d, e) = > a + b + c + d + e
constsum = sum(... numbers)Copy the code

ES2018 introduces residual properties, the same operator but only for objects.

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 }
Copy the code

Spread properties allow us to combine properties with… The attributes of the object after the operator:

constitems = { first, second, ... others } items//{ first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }
Copy the code

Set

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

A Set is a collection of objects or underlying data types (strings, numbers, or Booleans). You can think of it as a Map, where values are the mapping keys and the Map value is always true.

Initialize a Set

A Set can be initialized as follows:

const s = new Set(a)Copy the code

Add an item to the Set

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

s.add('one')
s.add('two')
Copy the code

Set stores only unique elements, so multiple calls to s.dd (‘one’) do not repeatedly add new elements.

You cannot add more than one element to a set at the same time. You need to call the add() method multiple times.

Check if the element is in the set

We can check if an element is in a set by:

s.has('one') //true
s.has('three') //false
Copy the code

Remove an element from a set:

Using the delete() method:

s.delete('one')
Copy the code

Determines the number of elements in a set

Use the size attribute:

s.size
Copy the code

Delete all elements from set

Using the clear() method:

s.clear()
Copy the code

Iterate over set

Use keys() or values() methods – these are equivalent to the following code:

for (const k of s.keys()) {
  console.log(k)
}
for (const k of s.values()) {
  console.log(k)
}
Copy the code

The Entries () method returns an iterator, which you can use as follows:

const i = s.entries()
console.log(i.next())
Copy the code

Calling i.ext () will return each element as a {value, done = false} object until the end of the iteration, when done is true.

You can also call the forEach() method of set:

s.forEach(v= > console.log(v))
Copy the code

Or you can just use for.. Of loop:

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

Initialize a set with some initializers

You can initialize a set with some values:

const s = new Set([1.2.3.4])
Copy the code

Convert a set to an array

const a = [...s.keys()]
// or
const a = [...s.values()]
Copy the code

WeakSet

A WeakSet is a special Set.

In a set, elements are not gc (garbage collection). A weakSet makes all of its elements gc ready. Each key in weakSet is an object. When the reference to this object disappears, the corresponding value can be gc.

Here are the main differences:

  1. WeakSet is not iterative
  2. You can’t empty all elements in weakSet
  3. Cannot get the size of weakSet

A weakSet is usually used in framework-level code and only exposes the following methods:

  • add()
  • has()
  • delete()

Map

A map structure of data allows us to establish relationships between data and keys

Before the ES6

Before the introduction of Map, developers usually used objects as maps, associating an Object or value value with a specified key:

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
Copy the code

After introducing Map

ES6 introduces Map data structures, which provide a suitable tool for working with such data structures

Map initialization:

const m = new Map(a)Copy the code

Add entries to the Map

You can set entries to a map using the set() method:

m.set('color'.'red')
m.set('age'.2)
Copy the code

Get entries from the map by key

You can retrieve items from the map using the get() method:

const color = m.get('color')
const age = m.get('age')
Copy the code

Delete entries from the map by key

Using the delete() method:

m.delete('color')
Copy the code

Delete all entries from the map

Using the clear() method:

m.clear()
Copy the code

Check whether the map contains an entry by the key value

Use the has() method

const hasColor = m.has('color')
Copy the code

Gets the number of entries in the map

Use the size attribute:

const size = m.size
Copy the code

Initialize a map with value

You can initialize a map with a set of values:

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

The key value of the Map

Any value (object, array, string, number) can be used as a map value (in the form of key-value keys), and any value can be used as a key, even an object.

If you want to get a nonexistent key from the map using the get() method, it will return undefined

Weird situations you’d be unlikely to find in the real world

const m = new Map()
m.set(NaN.'test')
m.get(NaN) //test
const m = new Map()
m.set(+0.'test')
m.get(0) //test
Copy the code

Use the Iterate iterator to get the keys of the map

Map provides the keys() method, which allows us to iterate over all keys:

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

Use the Iterate iterator to get the map values

Map provides the values() method, through which we can iterate out all values:

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

Retrieves key-value pairs using the Iterate iterator

Map provides the entries() method, through which we can iterate out all key-value pairs:

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

The use method can also be simplified as:

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

Convert the map keys to an array

const a = [...m.keys()]
Copy the code

Convert map values to arrays

const a = [...m.values()]
Copy the code

WeakMap

WeakMap is a special Map

In a map object, data defined on it will never be garbage collected. WeakMap instead allows the data defined on it to be garbage collected freely. Each key of WeakMap is an object, and when the pointer to the object is lost, the corresponding value will be garbage collected.

This is the main difference of WeakMap:

  1. You cannot iterate over keys and values (or key-value pairs) on WeakMap.
  2. You cannot remove all items from WeakMap
  3. You cannot get the size of WeakMap

WeakMap provides the following methods, which are used in the same way as in Map:

  • get(k)
  • set(k, v)
  • has(k)
  • delete(k)

The use case for WeakMap is not as obvious as the Map use case, and you will probably never use it anywhere, but as a practical matter, WeakMap can build memory-sensitive caches that don’t interfere with the garbage collection mechanism, as well as meet the need for encapsulation rigor and information hiding.

Generators generator

Generators are a special function that can pause its own execution and resume after a certain period of time, allowing other code to run in the meantime (see the complete “javascript Generator Guide” for details on this topic).

Generators’ code decides that it must wait, so it allows other code in the queue to run, reserving the right to resume its operation “when what it’s waiting for” is done.

All of this is done with a simple keyword, ‘yield’. Execution stops when the generator contains the keyword.

Generator generators can contain a number of yield keywords to stop themselves multiple times, which is identified by the *function keyword (not to be confused with the dereferencing operators used in low-level languages such as C, C++, or Go).

Generators support new programming paradigms in JavaScript, including:

  • Supports bidirectional communication when the Generator is running
  • Does not “freeze” a while loop that runs long in a program

Here is an example explaining how the generator works:

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

Let’s initialize it first:

const calc = calculator(10)
Copy the code

We then begin iterator iteration in generator:

calc.next()
Copy the code

The first iterator starts the iteration and returns the following object:

{
  done: false
  value: 5
}
Copy the code

Here’s how it works: The code runs the function and passes input=10 to the generator constructor, which runs until yield is reached and returns the yield output: input / 2 = 5, so we get a value of 5 and tell the iterator that it hasn’t done yet (the function just paused).

At the second iteration, we type 7:

calc.next(7)
Copy the code

Then we get the result:

{
  done: false
  value: 14
}
Copy the code

7 is used as the doubleThat value. Note that you might take input/2 as an input parameter, but this is only the return value for the first iteration. Now we ignore it, use the new input value 7, and multiply it by 2.

Then, we get the second yield value, which returns doubleThat, so the return value is 14.

For the next and final iterator, we type 100

calc.next(100)
Copy the code

In this way we get:

{
  done: true
  value: 14000
}
Copy the code

When the iterator completes (with no more yield keywords), we return input * doubleThat * another, which is equivalent to 10 * 14 * 100.


These are all features introduced in ES2015, but now let’s take a closer look at ES2016, which has a much smaller scope.


Array.prototype.includes()

This feature introduces a more concise syntax for checking whether an array contains a specified element.

For ES6 and earlier, to check whether an array contains a specified element, you have to use the indexOf method, which checks the index in the array. If the element does not exist, it returns -1. Since -1 evaluates to true, you need to reverse it, as shown in the following example:

if(! [1.2].indexOf(3)) {
  console.log('Not found')}Copy the code

With the new features introduced in ES7, we can do this:

if(! [1.2].includes(3)) {
  console.log('Not found')}Copy the code

Exponentiation operator

The exponentiation operator ** is equivalent to the math.pow () method, but it is not a library, but a language mechanism:

Math.pow(4.2) = =4支那2
Copy the code

This feature is a nice enhancement for mathematically intensive programs, where ** operators are standard in many languages (including Python, Ruby, MATLAB, Perl, and many others).


These are all features introduced in 2016, so let’s move on to 2017.


String padding

The purpose of string padding is to add characters to a string so that it reaches a specified length.

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

padStart(targetLength [, padString])
padEnd(targetLength [, padString])
Copy the code

Use examples:

Object.values()

This method returns an array containing all the properties of the object itself, as follows:

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

Object.values() can also apply to arrays:

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

Object.entries()

This method returns an array containing all of the object’s own attribute key-value pairs. It is an array of the form [key, value], as follows:

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

Object.entries() can also be used on arrays:

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

Object.getOwnPropertyDescriptors()

This method returns all of its (non-inherited) property descriptors. Any object in JavaScript has a set of properties, and each property has a descriptor. A descriptor is a set of attributes for an attribute, consisting of the following parts:

  • Value: a familiar value
  • Writable: Whether the property can be changed
  • Get: The getter function for the property, called when the property is read
  • Set: the setter function for the property, called when the property sets a value
  • If the 64x is false, the property cannot be deleted, and any property other than its value cannot be changed.
  • Enumerable: Whether this property can be enumerable

Object. GetOwnPropertyDescriptors (obj) accept an Object, and returns an Object with a descriptor set.

In what way is this useful?

ES6 gives us the object.assign () method, which copies all enumerable property values from one or more objects and returns a new Object.

However, there is a problem with this, because it does not correctly copy an attribute with a non-default attribute value.

If the Object has only one setter, then it will not correctly copy to a new Object. Use object.assign () to do the following:

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

This will not work:

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

But this will work:

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

With a simple console, you can view the following code:

person1.name = 'x'
"x"
person2.name = 'x'
person3.name = 'x'
"x"
Copy the code

Person2 has no setter, which cannot be copied in, and the shallow copy qualification for the Object is also present in the ** object.create ()** method.

The tail comma

This feature allows a trailing comma when a function is defined, and a trailing comma when a function is used:

const doSomething = (var1, var2,) = > {
  / /...
}
doSomething('test2'.'test2'.)Copy the code

This change will encourage developers to stop the ugly habit of writing commas at the beginning of a line

An asynchronous function

JavaScript has evolved from callback functions to Promise functions (ES2015) in a very short time, and since ES2017 the async/ Wait syntax of asynchronous JavaScript has become much simpler. An asynchronous function is a combination of a Promise and a generator. Basically, it is a higher-level abstraction than a Promise, and I repeat the general: async/await is built based on a Promise

Why async/await

It reduces references around promises and breaks the promise “don’t break the chain call” constraint.

When Promise was introduced in ES2015, it was meant to solve the problem of asynchronous code, and it did, but in the two years between ES2015 and ES2017, it became clear that Promise wasn’t the ultimate solution.

Promise was introduced to solve the famous callback hell, but it also introduces its own usage and syntactic complexities.

Promise was a nice native feature around which developers could explore better syntax, so when the time came, we got async functions

Async functions make code look like synchronous functions, but behind the scenes they are asynchronous and non-blocking.

How it works

An async function returns a promise, as in the following example:

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

When you want to call this function, you put a wait in front of it, and the call is stopped until the promise is resolved or rejected. Note that the outer function must be defined as async. Here’s an example:

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

A hands-on example

Here is a simple example of an asynchronous function with async/await:

const doSomethingAsync = (a)= > {
  return new Promise(resolve= > {
    setTimeout((a)= > resolve('I did something'), 3000)})}const doSomething = async() = > {console.log(await doSomethingAsync())
}
console.log('Before')
doSomething()
console.log('After')
Copy the code

The above code will print the following result in the browser’s console:

Before
After
I did something //after 3s
Copy the code

On the Promise

Marking async keyword on any function means that the function will return a Promise, even if the function does not explicitly return a Promise internally, which is why the following code works:

const aFunction = async() = > {return 'test'
}
aFunction().then(alert) // This will alert 'test'
Copy the code

The same goes for the following example:

const aFunction = async() = > {return Promise.resolve('test')
}
aFunction().then(alert) // This will alert 'test'
Copy the code

Easier to read code

As in the example above, we compare it to a normal callback or chained function, and our code looks very simple.

This is a simple example, and when the code is complex enough, it generates more revenue.

For example, use a Promise to get a JSON resource and parse it:

const getFirstUserData = (a)= > {
  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()
Copy the code

Here is an example of doing the same thing with async/await:

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()
Copy the code

Serial multiple asynchronous functions

Async functions are very easy, and their syntax is much more readable than Promise.

const promiseToDoSomething = (a)= > {
  return new Promise(resolve= > {
    setTimeout((a)= > 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)
})
Copy the code

Print result:

I did something and I watched and I watched as well
Copy the code

Easier debugging

Debugging promises is difficult because the debugger can’t cross asynchronous code, but debugging async/await is very simple and the debugger will handle it as if it were synchronous code.

Shared memory and atoms

WebWorkers can create multithreaded programs in a browser.

They pass messages in the form of events, and starting with ES2017, you can use SharedArrayBuffer to share an array of memory between each Worker and their creator.

Atomics avoids race conditions because it is not known how long it takes to write parts of memory to broadcast, so any type of write is completed when the value is read.

More details about it can be found in the proposal.


This is ES2017, and I’m going to introduce the features of ES2018.


Rest/Spread Properties

ES2015 introduces methods for deconstructing arrays when you use:

const numbers = [1.2.3.4.5]
[first, second, ...others] = numbers
Copy the code

And expand parameters:

const numbers = [1.2.3.4.5]
const sum = (a, b, c, d, e) = > a + b + c + d + e
constsum = sum(... numbers)Copy the code

ES2018 introduces the same functionality for objects.

Deconstruction:

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 }
Copy the code

Expand properties allow new objects to be created by combining object properties passed after the expand operator:

constitems = { first, second, ... others } items//{ first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }
Copy the code

Asynchronous iterator

For-await-of allows you to iterate through loops using asynchronous iterables:

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

Since it uses await, you can only use it in async functions.

Promise.prototype.finally()

This is a big pity. When a Promise is fulfilled, it will invoke then one after another.

If an error occurs during this process, then is skipped and a catch is executed.

And finally() allows you to run some code, with or without success:

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

Regular expression improvement

ES2018 to regular expressions, introduced many improvements, these can be in flaviocopes.com/javascript-… Found on.

Here are some specific additions to the ES2018 regular expression improvements:

RegExp lookbehind assertions: Matches a string based on previous assertions

Is this a Lookahead: what can you use? = to match a string, followed by a specific string:

/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
Copy the code

? ! It is possible to do the reverse if the matching string is no instead of following a specific substring afterwards:

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

Lookaheads used? = Symbol, they are ready to use.

Is Lookbehinds a new feature to use? < =.

/ (?<=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
Copy the code

If a lookbehind is negative, use? >! :

/ (?<! 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
Copy the code

Unicode attribute escape\ p {... } and \ P {... }

In regular expression mode, you can use \d to match any number, \s to match any string that isn’t a space, \w to match any alphanumeric string, and so on.

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

This new feature extends unicode characters, introducing \p{} for handling

Any Unicode character has a set of attributes, such as the script acknowledgment language, and ASCII is a Boolean value used to check for ASCII characters. You can place this attribute in () and the regular expression will check to see if it is true.

/^\p{ASCII}+$/u.test('abc')   / / ✅
/^\p{ASCII}+$/u.test('ABC@')  / / ✅
/^\p{ASCII}+$/u.test('ABC 🙃') / / ❌
Copy the code

ASCII_Hex_Digit is another Boolean value used to check if the string contains a valid hexadecimal number:

/^\p{ASCII_Hex_Digit}+$/u.test('0123456789ABCDEF') / / ✅
/^\p{ASCII_Hex_Digit}+$/u.test('h')                / / ❌
Copy the code

In addition, there are many other attributes. You can check them by adding their names to (), 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('🙃 🙃') / / ✅
Copy the code

In addition to binary attributes, you can also check any Unicode character attributes to match specific values, in this case I check if the string is written in Greek or Latin:

/^\p{Script=Greek}+$/u.test('epsilon lambda lambda eta argument ι kappa ά predominate') / / ✅
/^\p{Script=Latin}+$/u.test('hey') / / ✅
Copy the code

Reading github.com/tc39/propos… Gets details about using all attributes.

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:

const re = / (? 
      
       \d{4})-(? 
       
        \d{2})-(? 
        
         \d{2})/
        
       
      
const result = re.exec('2015-01-02')
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';
Copy the code

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:

/hi.welcome/.test('hi\nwelcome') // false
/hi.welcome/s.test('hi\nwelcome') // true
Copy the code

ESNext

What is ESNext?

ESNext is a name that always points to the next version of JavaScript.

The current version of ECMAScript is ES2018, which was released in June 2018.

Historically, standardized versions of JavaScript have been released in the summer, so we can expect ECMAScript 2019 to be released in the summer of 2019.

So ES2018 has been released at the time of this writing, so ESNext refers to ES2019.

Proposals for the ECMAScript standard are organized in phases, with phases 1 through 3 being incubated for functionality, and phase 4 functionality finalized as part of the new standard.

At the time of writing this article, the major browsers implemented most of the features of Phase 4, so I’ll cover them in this article.

Some of these changes are mostly for internal use, but it’s also good to know what’s going on.

There are a few other phase 3 features that are likely to be upgraded to Phase 4 in the coming months, and you can check them out at this Github repository: github.com/tc39/propos… .

Array.prototype.{flat,flatMap}

Flat () is a new array instance method that converts multidimensional arrays into one-dimensional arrays.

Example:

['Dog'['Sheep'.'Wolf']].flat()
//[ 'Dog', 'Sheep', 'Wolf' ]
Copy the code

By default it can only convert a two-dimensional array to a one-dimensional array, but you can add a parameter to determine the level to expand. If you set this parameter to Infinity it will expand an infinite number of levels to a one-dimensional array:

['Dog'['Sheep'['Wolf']]].flat()
//[ 'Dog', 'Sheep', [ 'Wolf' ] ]
['Dog'['Sheep'['Wolf']]].flat(2)
//[ 'Dog', 'Sheep', 'Wolf' ]
['Dog'['Sheep'['Wolf']]].flat(Infinity)
//[ 'Dog', 'Sheep', 'Wolf' ]
Copy the code

If you’re familiar with the map method of arrays, you know that you can use it to perform a function on each element of an array.

FlatMap () is a new array instance method that combines flat() with map. This is useful when you want to do some processing in a map function, but still want the result to look like 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' ]
Copy the code

Optional catch binding

Sometimes we don’t need to bind parameters to try/catch.

In the old days we had to do this:

try {
  / /...
} catch (e) {
  //handle error
}
Copy the code

Even though we have never analyzed errors by e, we can now simply omit it:

try {
  / /...
} catch {
  //handle error
}
Copy the code

Object.fromEntries()

Objects have an entries() method, since ES2017.

Starting with ES2017, Object will have an entries() method.

It returns an array containing an array of all the object’s own attributes, such as [key, value] :

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

ES2019 introduces a new object.fromentries () method that creates a new Object from the property array above:

const person = { name: 'Fred'.age: 87 }
const entries = Object.entries(person)
const newPerson = Object.fromEntries(entries) person ! == newPerson//true
Copy the code

String.prototype.{trimStart,trimEnd}

These features have been implemented in V8 /Chrome for almost a year and will be standardized in ES2019.

trimStart()

Delete the space at the beginning of the string and return a new string:

'Testing'.trimStart() //'Testing'
' Testing'.trimStart() //'Testing'
' Testing '.trimStart() //'Testing '
'Testing'.trimStart() //'Testing'
Copy the code

trimEnd()

Remove the space at the end of the string and return a new string:

'Testing'.trimEnd() //'Testing'
' Testing'.trimEnd() //' Testing'
' Testing '.trimEnd() //' Testing'
'Testing '.trimEnd() //'Testing'
Copy the code

Symbol.prototype.description

Now you can use description to get the value of Symbol instead of using the toString() method:

const testSymbol = Symbol('Test')
testSymbol.description // 'Test'
Copy the code

JSON improvements

The delimiter (\u2028) and delimiter (\u2029) are not allowed in the JSON string before this.

When using json.parse, these characters cause a SyntaxError, but now they parse correctly and as defined by the JSON standard.

Well-formed JSON.stringify()

Fixed json.stringify () when handling UTF-8 code points (U+D800 to U+DFFF).

Before the fix, calling json.stringify () will return a badly formed Unicode character, such as (a “�”).

Now you can safely convert it to a string using json.stringify (), and you can convert it back to its original representation using json.parse ().

Function.prototype.toString()

A function always has a toString method, which returns a string containing the function code.

ES2019 changes the return value to avoid stripping comments and other strings (such as Spaces) and to more accurately represent the function definition.

If previously we had

Maybe we used to have this:

function/ *this is bar* /bar () {}
Copy the code

Behavior at the time:

bar.toString() //'function bar() {}
Copy the code

Current behavior:

bar.toString(); // 'function /* this is bar */ bar () {}'
Copy the code

In conclusion, I hope this article has helped you get up to speed on some of the latest JavaScript and what we’ll see in 2019.

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

Copyright

Copyright: Lightning Miners Translation group translation is for study, research and communication only. The copyright belongs to The Lightning Miners Translation Group, the author of the article and the translator. Non-commercial reprint is welcome. Please contact the translator or administrator for permission before reprinting, and clearly indicate the source of this article, translator, proofreader and full links to the Lightning Miners Translation Group at the beginning of the article, offenders will be corrected.