As we all know, ECMAScript iterates very quickly, and as a front-end developer, we must be familiar with the new syntax of ES6 in recent years, but some of the new ECMAScript proposals in ES7 through ES12 may not be widely known to developers. This article takes a look at some of the ECMAScript syntax and proposals that will be added between 2016 and 2021 to help you adapt them to your own projects.

TC39 specification introduction:

Stage 0 (strawman), which can be submitted by any TC39 member.

Stage 1 (proposal), which means that the proposal is considered official and requires a detailed description of the scenario and API for the proposal.

Stage 2 (draft) : Proposals in this Stage will not change much in subsequent stages if they make it to the standard, since only incremental changes are theoretically accepted.

Stage 3 (candidate), a Stage in which proposals are modified only when major problems are encountered and the specification document needs to be fully completed.

Stage 4 (Finished) proposals from this Stage will be incorporated into the specification that ES releases each year.

ES7 (ES2016)

1, the Array. The prototype. Includes

An overview of the

The includes() method is used to determine whether an array contains a specified value, returning true if it does and false otherwise.

The proposal is currently in Stage 4.

TC39 express

motivation

When using ECMAScript arrays, you often need to determine whether the array contains an element. The common approach is:

if(arr.indexOf(el) ! = = -1) {... }Copy the code

Strictly speaking, using indexOf to determine whether an array contains an element is problematic:

  • We want to know if the array contains an element, not the first occurrence index of an element in the array.
  • rightNaNDon’t take effect,[NaN].indexOf(NaN) === -1.

grammar

arr.includes(valueToFind[, fromIndex])

  • valueToFind: Indicates the value of the element to be searched.
  • fromIndex: Optional. fromfromIndexThe search begins at the indexvalueToFind. If it is negative, the value is in ascending order fromarr.length + fromIndexJump forward from the end of the indexfromIndexAnd then search back). The default is0.

Example:

[1.2.3].includes(2);     // true
[1.2.3].includes(4);     // false
[1.2.3].includes(3.3);  // false
[1.2.3].includes(3, -1); // true
[1.2.NaN].includes(NaN); // true
Copy the code

Matters needing attention

1. If fromIndex is greater than or equal to the length of the array, false is returned and the array is not searched.

const arr = ['a'.'b'.'c'];
arr.includes('c'.3);   // false
arr.includes('c'.100); // false
Copy the code

2. If fromIndex is negative, the calculated index will be the place to start searching for valueToFind. If the calculated index is less than 0, the entire array is searched.

// array length is 3
// fromIndex is -100
// computed index is 3 + (-100) = -97

const arr = ['a'.'b'.'c'];

arr.includes('a', -100); // true
arr.includes('b', -100); // true
arr.includes('c', -100); // true
arr.includes('a', -2);   // false
Copy the code

3. The includes() method is intentionally designed as a universal method. It does not require the this value to be an array object, so it can be used for other types of objects (such as array-like objects). The following example shows calling an includes() method on the arguments object of a function.

(function() {
  console.log([].includes.call(arguments.'a')); // true
  console.log([].includes.call(arguments.'d')); // false}) ('a'.'b'.'c');
Copy the code

2, Exponentiation Operator

An overview of the

The exponentiation operator (**) returns the result of adding the first operand to the power of the second. It is equivalent to math.pow, except that it also accepts BigInt as its operand.

The proposal is currently in Stage 3.

TC39 express

grammar

Operator: var1 ** var2

The exponentiation operator is right-associative: a ** b ** c is equal to a ** (b ** c).

Example:

2六四事件3   / / 8
3六四事件2   / / 9
3六四事件2.5 / / 15.588457268119896
10* * -1 / / 0.1
NaN六四事件2 // NaN

2六四事件3六四事件2   / / 512
2* * (3六四事件2) / / 512
(2六四事件3) * *2 / / 64- (2六四事件2) / / - 4

(-2) * *2 / / 4
Copy the code

ES8 (ES2017)

Async and await

An overview of the

The async and await keywords are syntactic candy based on Generator functions, making asynchronous code easier to write and read. By using them, asynchronous code looks more like old-fashioned synchronous code.

TC39 express

motivation

Simplify asynchronous programming.

Using the Generator function, read two files in turn:

const fs = require('fs');

const readFile = function (fileName) {
  return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data) {
      if (error) return reject(error);
      resolve(data);
    });
  });
};

const gen = function* () {
  const f1 = yield readFile('/etc/fstab');
  const f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

Copy the code

Gen can be written as async, like this:

const asyncReadFile = async function () {
  const f1 = await readFile('/etc/fstab');
  const f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
Copy the code

A comparison shows that async simply replaces the asterisk (*) of Generator functions with async and yields with await.

The improvements of async over Generator are shown in the following four aspects:

  • Built-in actuators. The execution of Generator functions must depend on the executor, hence the GeneratorcoModule, andasyncFunctions come with their own actuators. In other words,asyncFunctions are executed exactly like normal functions, with only one line.
  • Better semantics.asyncawait, compared to the asterisk (*) andyieldThe semantics are clearer.asyncIt means that there are asynchronous operations in the function,awaitIndicates that the following expression needs to wait for the result.
  • Wider applicability.coModule convention,yieldCommand can only be followed byThunkA function orPromiseObject, andasyncFunction of theawaitAfter the command, it can bePromiseObject and primitive type values (numeric, string, Boolean, and so on), but this is automatically converted to immediateresolvedPromiseObject).
  • The return value is Promise.asyncThe function returns a Promise object, which is much more convenient than Generator functions that return an Iterator. You can usethenMethod specifies what to do next. And going further,asyncFunctions can be thought of as asynchronous operations wrapped into a single Promise object, whileawaitThe command is the insidethenSyntax sugar for commands.

grammar

async function getData() {
  const res = await api.getTableData(); // await asynchronous tasks
  // do something
}
Copy the code

Example:

async function getStockPriceByName(name) {
  const symbol = await getStockSymbol(name);
  const stockPrice = await getStockPrice(symbol);
  return stockPrice;
}

getStockPriceByName('goog').then(function (result) {
  console.log(result);
});
Copy the code

2, Object.values and object. entries

An overview of the

The object.values () method returns an array of all the enumerable property values of a given Object itself, in the same order as the values used for… The in loop is in the same order. The in loop enumerates properties in the stereotype chain.

The object.entries () method returns an array of key-value pairs for the given Object’s own enumerable properties, arranged in the same order as for… The in loop returns the same order as it iterates through the object. The IN loop also enumerates properties in the stereotype chain.

The proposal is currently in Stage 4.

TC39 express

grammar

Object.values(obj)

  • Obj: Object that is returned with an enumerable property value.

Object.entries(obj)

  • Obj: An object that can return key-value pairs of its enumerable properties.

Example:

Object.values({a1.b2.c3});  / / [1, 2, 3]
Object.keys({a1.b2.c3});    // [a, b, c]
Object.entries({a1.b2.c3}); // [["a", 1], ["b", 2], ["c", 3]]
Copy the code

3, Object. GetOwnPropertyDescriptors

An overview of the

Gets the descriptor for all of an object’s own attributes, or returns an empty object if there are none.

The proposal is currently in Stage 4.

TC39 express

motivation

There is no method in ECMAScript that simplifies copying between two objects. Today, functional programming and immutable objects are an important part of complex applications, and each framework or library implements its own boilerplate to properly copy properties between composite objects or prototypes.

Going back to Object.assign, it most often exhibits some undesirable behavior because its replication devours behavior: it accesses attributes and symbols directly rather than their descriptors, discarding possible accessors, which can be dangerous when combining prototypes of more complex objects or classes.

Retrieving all descriptors, enumerable or not, is also key to implementing combinations of classes and their prototypes, since they have non-enumerable methods and accessors by default.

In addition, decorators can easily get all descriptors from a class or mixin and filter unwanted descriptors through Object.defineProperties.

Object.assign is only suitable for making shallow copies of objects.

grammar

Object.getOwnPropertyDescriptors(obj)

  • Obj: Any object

Example:

const a = { 
  b: 123.c: 'Textual content'.d() { return 2; }};Object.getOwnPropertyDescriptors(a);
// Output the result
{
  b: {configurable: true.enumerable: true.value: 123.writable: true,
    [[Prototype]]: Object,}c: {configurable: true.enumerable: true.value: "Textual content".writable: true,
    [[Prototype]]: Object,}d: {configurable: true.enumerable: true.value: ƒ d(),
    writable: true,
    [[Prototype]]: Object,
  },
  [[Prototype]]: Object,}Copy the code

4, the String. The prototype. PadStart and String. Prototype. PadEnd

An overview of the

The padStart() method populates the current string with another string (repeated multiple times if necessary) so that the resulting string reaches the given length. Padding starts at the beginning of the current string (left).

The padEnd() method populates the current string with a string (or repeats if necessary) and returns the string that has been filled to the specified length. Populate from the end of the current string (right).

The proposal is currently in Stage 4.

TC39 express

grammar

str.padStart(targetLength [, padString])

  • targetLength: The target length to fill the current string. If the value is less than the length of the current string, the current string itself is returned.
  • padString: Optional. Padding string. If the string is too long and the length of the filled string exceeds the target length, only the leftmost part is retained and the rest is truncated. The default value of this parameter is""(U + 0020).

str.padEnd(targetLength [, padString])

  • targetLength: The target length to fill the current string. If the value is less than the length of the current string, the current string itself is returned.
  • padString: Optional. Padding string. If the string is too long and the length of the filled string exceeds the target length, only the leftmost part is retained and the rest is truncated. The default value of this parameter is""(U + 0020).

Example:

'abc'.padStart(10);         // " abc"
'abc'.padStart(10."foo");  // "foofoofabc"
'abc'.padStart(6."123465"); // "123abc"
'abc'.padStart(8."0");     // "00000abc"
'abc'.padStart(1);          // "abc"
Copy the code
'abc'.padEnd(10);          // "abc "
'abc'.padEnd(10."foo");   // "abcfoofoof"
'abc'.padEnd(6."123456"); // "abc123"
'abc'.padEnd(1);           // "abc"
Copy the code

ES9 (ES2018)

1, for await… of

An overview of the

for await… The of statement creates a loop that iterates over both asynchronous and synchronous iterables, including: Built-in String, Array, array-like objects (such as Arguments or NodeList), TypedArray, Map, Set, and user-defined asynchronous/synchronous iterators. It invokes the custom iterative hook using the statement to execute with the value of each different attribute of the object.

Like the await operator, this statement can only be used inside an async function.

TC39 express

motivation

The iterator interface (introduced in ECMAScript 2015) is a sequential data access protocol that enables the development of common and composable data consumers and converters. Since the iterator method must know the next value in the sequence and the “done” state of the data source when it returns, iterators are only suitable for representing synchronous data sources. While many data sources encountered by JavaScript programmers are synchronous (such as in-memory lists and other data structures), many others are not. For example, any data source that requires I/O access is typically represented using an event-based or streaming asynchronous API. Unfortunately, iterators cannot be used to represent such data sources.

(Even the Promise iterator is insufficient, since it only allows for asynchronous determination of values, but requires synchronous determination of the “done” state.)

In order to provide a common data access protocol for asynchronous data sources, the asynchronous iteration statement for await… Of.

grammar

async function process(iterable) {   for await (variable of iterable) { // Do something. } }

  • variable: In each iteration, the values of different attributes are assigned to variables. Variables may be declared as const, let, or var.
  • 可迭代: An object whose properties are iteratively enumerated. And for… In contrast, the object here can return a Promise, if so, variable will be the value contained in the Promise, otherwise the value itself.

Example:

function getTime(seconds){
  return new Promise(resolve= >{
    setTimeout(() = >{ resolve(seconds) }, seconds); })}async function test(){
  let arr = [getTime(500), getTime(300), getTime(3000)];
  for await (let x of arr){
    console.log(x); // 500 300 3000 in order
  }
}

test()
Copy the code

2, Promise. Prototype. Finally

An overview of the

The finally() method returns a Promise. At the end of the Promise, the specified callback function will be executed regardless of the result, which is fulfilled or Rejected. This provides a way for code to be executed after a Promise is successfully completed or not.

This avoids the need to write the same statement once in both then() and catch().

TC39 express

motivation

Sometimes when a Promise is fulfilled, we want to perform some cleaning work or set some variables, which is a pity or rejected. At this time, we have to write the same code twice in resolve and Catch, which is repetitive and meaningless work. Promise. Finally () can be implemented when the Promise is fulfilled, no matter what the result is, which is a pity or rejected.

grammar

Promise.resolve().then().catch(e => e).finally(onFinally);

  • onFinally: Function to call after Promise ends.

Example:

let isLoading = true;
fetch(myRequest).then(function(response) {
  const contentType = response.headers.get("content-type");
  if(contentType && contentType.includes("application/json")) {
    return response.json();
  }
  throw new TypeError("Oops, we haven't got JSON!");
})
.then(function(json) { /* process your JSON further */ })
.catch(function(error) { console.log(error); })
.finally(function() { isLoading = false; });
Copy the code

Matters needing attention

Finally () is similar to.then(onFinally, onFinally), but they differ in that:

  • When you call an inline function, you don’t need to declare the function multiple times or create a variable for the function to hold it.
  • Since there is no way to know the final state of the promise, no arguments are taken in the finally callback function, which is only used when the final result is to be executed regardless.
  • withPromise.resolve(2).then(() => {}, () => {})‘Resolved’Promise.resolve(2).finally(() => {})Resolved results were 2.
  • In the same way,Promise.reject(3).then(() => {}, () => {})This is a big pity. (undefined),Promise.reject(3).finally(() => {})The result of Rejected is 3.

3, Object Rest/Spread Properties

An overview of the

Add Rest syntax to the deconstruction. Rest attributes collect the remaining enumerable property keys that have not yet been picked up by the deconstruction pattern.

Add the Spread syntax to the object literal. Copy all the Enumerable properties of an existing object into the newly constructed object.

The proposal is currently in Stage 4.

TC39 express

motivation

Shallow-copy (no prototype) and object merge, using the shorter Spread syntax. Instead of using object.assign ().

const obj1 = { foo: 'bar'.x: 42 };
const obj2 = { foo: 'baz'.y: 13 };

constclonedObj = { ... obj1 };// Clone object: {foo: "bar", x: 42}

constmergedObj = { ... obj1, ... obj2 };{foo: "baz", x: 42, y: 13}
Copy the code

Rest syntax can greatly simplify variable extraction when we deconstruct an object and only care about one or two of its properties.

const{ x, y, ... z } = {x: 1.y: 2.a: 3.b: 4 };
x; / / 1
y; / / 2
Copy the code

grammar

Rest

const{ x, y, ... z } = {x: 1.y: 2.a: 3.b: 4 };
x; / / 1
y; / / 2
z; // { a: 3, b: 4 }
Copy the code

Spread

letn = { x, y, ... z }; n;// { x: 1, y: 2, a: 3, b: 4 }
Copy the code

Matters needing attention

The object.assign () function fires setters, but the Spread syntax does not.

ES10 (ES2019)

1, Array.prototype.flat and array.prototype. flatMap

An overview of the

The Flat () method recurses through the array of numbers at a specified depth and returns all the elements in a new array combined with the elements in the traversed subarray.

The flatMap() method first maps each element using a mapping function and then compresses the result into a new array.

Flat with a depth of 1 is almost the same when the flatMap method is combined with the Map method.

The proposal is currently in Stage 4.

TC39 express

motivation

If we want to flatten a multilayer nested array, the code looks like this:

// Use reduce, concat, and recursion to expand an infinite number of nested arrays
const arr = [1.2.3[1.2.3.4[2.3.4]]];

function flatDeep(arr, d = 1) {
  return d > 0 ? arr.reduce((acc, val) = > acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val), []) : arr.slice();
};

flatDeep(arr, Infinity);
// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
Copy the code

The Flat proposal is designed to simplify this process:

const arr = [1.2.3[1.2.3.4[2.3.4]]];
arr.flat(Infinity);
// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
Copy the code

If we wanted to split an array of several sentences into a new array of single words, the code would look like this:

const arr = ["it's Sunny in".""."California"];
const arr1 = arr.map(x= > x.split(""));
// [["it's","Sunny","in"],[""],["California"]];
arr1.flat(2);
// ["it's", "Sunny", "in", "", "California"]
Copy the code

The flatMap proposal can be used to simplify this process:

const arr = ["it's Sunny in".""."California"];
arr.flatMap(x= > x.split(""));
// ["it's", "Sunny", "in", "", "California"]
Copy the code

grammar

arr.flat([depth])

  • depth: Optional. Specifies the structural depth to extract the nested array. The default value is1.

Example:

[1.2[3.4]].flat(1); 
// [1, 2, 3, 4]

[1.2[3.4[5.6]]].flat(2); 
// [1, 2, 3, 4, 5, 6]

[1.2[3.4[5.6]]].flat(Infinity); 
// [1, 2, 3, 4, 5, 6]
Copy the code

arr.flatMap( function callback( currentValue[, index[, array]]) { // return element for new_array }[, thisArg])

  • callbackA function that generates elements in a new array, passing in three arguments:
    • currentValue: The element currently being processed in the array.
    • index: Optional. The index of the current element being processed in the array.
    • array: Optional. The index of the current element being processed in the array.
  • thisArg: Optional. Used when executing the callback functionthisValue.

Example:

[1.2.3.4].flatMap(a= > [a ** 2]); 
// [1, 4, 9, 16]
Copy the code

The map () and flatMap ()

const arr1 = [1.2.3.4];

arr1.map(x= > [x * 2]);
[[2], [4], [6], [8]]

arr1.flatMap(x= > [x * 2]);
// [2, 4, 6, 8]

arr1.map(x= > [x * 2]).flat(1);
// [2, 4, 6, 8]

// only one level is flattened
arr1.flatMap(x= > [[x * 2]]);
[[2], [4], [6], [8]]
Copy the code
let arr1 = ["it's Sunny in".""."California"];

arr1.map(x= > x.split(""));
// [["it's","Sunny","in"],[""],["California"]]

arr1.flatMap(x= > x.split(""));
// ["it's","Sunny","in", "", "California"]
Copy the code

2, the Object. FromEntries

An overview of the

The object.fromentries () method converts the list of key-value pairs to an Object.

Object.fromentries () performs the reciprocal operation with Object.entries.

The proposal is currently in Stage 4.

TC39 express

motivation

const obj = { foo: true.bar: false };
const map = new Map(Object.entries(obj));
Copy the code

To convert a map like the one above to obj, we would have to write a helper function:

const obj = Array.from(map).reduce((acc, [key, val]) = > Object.assign(acc, { [key]: val }), {});
Copy the code

The Object. FromEntries proposal is designed to simplify this process.

grammar

Object.fromEntries(iterable);

  • 可迭代: an iterable similar to Array, Map, or other iterable that implements the iterable protocol.

Example:

Nested array

const obj = Object.fromEntries([['a'.0], ['b'.1]]); 
// { a: 0, b: 1 }
Copy the code

Object.entries

const obj = { abc: 1.def: 2.ghij: 3 };
const res = Object.fromEntries(
  Object.entries(obj)
  .filter(([key, val]) = > key.length === 3)
  .map(([key, val]) = > [key, val * 2]));// { abc: 2, def: 4 }
Copy the code

FromEntries converts a Map to an Object:

const map = new Map([['a'.1], ['b'.2], ['c'.3]]);
const obj = Object.fromEntries(map);
// { a: 1, b: 2, c: 3 }
Copy the code

3, String. Prototype. TrimStart and String. Prototype. TrimEnd

An overview of the

Removes successive whitespace at the beginning and end of a string.

The trimStart() method removes whitespace from the beginning of the string. TrimLeft () is an alias for this method.

The trimEnd() method removes whitespace from the end of the string. TrimRight () is an alias for this method.

The proposal is currently in Stage 4.

TC39 express

motivation

ES5 standardizes string.prototype.trim. All major search engines also implement the corresponding trimLeft and trimRight functions, but there is no standard specification. To be consistent with padStart/padEnd, trimStart and trimEnd are proposed. For compatibility issues between browsers, trimLeft/trimRight will be used as an alias for trimStart/trimEnd.

String.prototype.trimLeft === String.prototype.trimStart;
// true
String.prototype.trimRight === String.prototype.trimEnd;
// true
Copy the code

grammar

const greeting = ' Hello world! ';
console.log(greeting.trimStart());
// "Hello world! ";
console.log(greeting.trimEnd());
// " Hello world!" ;
Copy the code

4. Optional Catch Binding

An overview of the

This proposal makes a syntactic change to ECMAScript to allow the catch binding to be omitted without the use of the catch binding.

The proposal is currently in Stage 4.

TC39 express

motivation

try {
  // try to use a web feature which may not be implemented
} catch (unused) {
  // fall back to a less desirable web feature with broader support
}
Copy the code
let isTheFeatureImplemented = false;
try {
  // stress the required bits of the web API
  isTheFeatureImplemented = true;
} catch (unused) {}
Copy the code

It is generally accepted that declaring or writing variables that have never been read can cause a program error.

grammar

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

5, Symbol. The prototype. The description

An overview of the

Symbol objects can be created from an optional description and can be used for debugging, but not for accessing the Symbol itself.

Symbol. The prototype. The description attribute can be used to read the description. And Symbol. The prototype. The toString () of different is that it will not include “Symbol ()” string.

Description is a read-only property that returns a string of optional descriptions of the Symbol object.

The proposal is currently in Stage 4.

TC39 express

motivation

Directly exposed symbol description of internal slot, replaced by the symbol. The prototype. ToString indirect exposure.

grammar

Symbol('myDescription').description;

Symbol.iterator.description;

Symbol.for('foo').description;
Copy the code

Example:

Symbol('desc').toString();  // "Symbol(desc)"
Symbol('desc').description; // "desc"
Symbol(' ').description;     / / ""
Symbol().description;       // undefined

// well-known symbols
Symbol.iterator.toString();  // "Symbol(Symbol.iterator)"
Symbol.iterator.description; // "Symbol.iterator"

// global symbols
Symbol.for('foo').toString();  // "Symbol(foo)"
Symbol.for('foo').description; // "foo"
Copy the code

ES11 (ES2020)

1, the String. The prototype. MatchAll

An overview of the

The matchAll() method returns an iterator containing the results of all matching regular expressions and the grouped capture groups.

TC39 express

motivation

Previously, if we wanted to get all the match information, we could only call regexp.exec() through a loop (regexp requires the /g flag) :

const regexp = RegExp('foo[a-z]*'.'g');
const str = 'table football, foosball';
let match;

while((match = regexp.exec(str)) ! = =null) {
  console.log(`Found ${match[0]} start = ${match.index} end = ${regexp.lastIndex}. `);
  // expected output: "Found football start=6 end=14."
  // expected output: "Found foosball start=16 end=24."
}
Copy the code

If you use matchAll, you don’t have to use the while loop with exec (and the regular expression uses the /g flag). Using matchAll returns an iterator with the return value for… Of, array spread, or array.from () can be more convenient.

grammar

str.matchAll(regexp)

  • regexp: Regular expression object. If the passed parameter is not a regular expression object, it is used implicitlynew RegExp(obj)Convert it to oneRegExp

RegExp must be in the form of global mode /g, otherwise TypeError will be raised.

Example:

const regexp = RegExp('foo[a-z]*'.'g');
const str = 'table football, foosball';
const matches = str.matchAll(regexp);

for (const match of matches) {
  console.log(`Found ${match[0]} start = ${match.index} end = ${match.index + match[0].length}. `);
}
// expected output: "Found football start=6 end=14."
// expected output: "Found foosball start=16 end=24."

// matches iterator is exhausted after the for.. of iteration
// Call matchAll again to create a new iterator
Array.from(str.matchAll(regexp), m= > m[0]);
// Array [ "football", "foosball" ]
Copy the code

Matters needing attention

1. MatchAll throws an exception if there is no /g flag.

const regexp = RegExp('[a-c]'.' ');
const str = 'abc';
Array.from(str.matchAll(regexp), m= > m[0]);
// TypeError: String.prototype.matchAll called with a non-global RegExp argument
Copy the code

MatchAll makes an internal copy of regexp, so unlike regexp.exec, lastIndex does not change during string scanning.

const regexp = RegExp('[a-c]'.'g');
regexp.lastIndex = 1;
const str = 'abc';
Array.from(str.matchAll(regexp), m= > `${regexp.lastIndex} ${m[0]}`);
// Array [ "1 b", "1 c" ]
Copy the code

3. Another highlight of matchAll is better capture groups. Because the capture group is ignored when matching information is obtained using the match() and /g flags:

const regexp = /t(e)(st(\d?) )/g;
const str = 'test1test2';

str.match(regexp);
// Array ['test1', 'test2']
Copy the code

Using matchAll, you can get the group capture as follows:

let array = [...str.matchAll(regexp)];

array[0];
// ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
array[1];
// ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]
Copy the code

2, the Dynamic Import

An overview of the

Dynamic import module

The proposal is currently in Stage 4.

TC39 express

motivation

The standard use of import is static, causing all imported modules to be compiled as soon as they are loaded (on-demand compilation is not possible, slowing the loading of the home page). You can use dynamic imports instead of static imports in scenarios where you might want to import modules conditionally or on demand. Here are some scenarios you might need to dynamically import:

  • When a statically imported module obviously slows down the loading speed of the code and is less likely to be used, it may not be needed immediately.
  • When statically imported modules clearly take up a lot of system memory and are less likely to be used.
  • When imported modules do not exist at load time, they need to be retrieved asynchronously.
  • When importing module specifiers, you need to build them dynamically. Static imports can only use static specifiers.
  • When an imported module has side effects (side effects, in this case, can be understood as code that runs directly in the module), these side effects are only needed if certain conditions are triggered. (In principle, modules can’t have side effects, but many times you can’t control the content of the modules you rely on.)

grammar

import()

The keyword import can be used to dynamically import modules as if they were functions. Called this way, a Promise is returned.

Example:

import('/modules/my-module.js')
  .then((module) = > {
    // Do something with the module.
  });
Copy the code

The await keyword is also supported

const module = await import('/modules/my-module.js');
Copy the code

Template string format

const main = document.querySelector("main");
  for (const link of document.querySelectorAll("nav > a")) {
    link.addEventListener("click".e= > {
      e.preventDefault();
      import(`./section-modules/${link.dataset.entryModule}.js`)
        .then(module= > {
          module.loadPageInto(main);
        })
        .catch(err= > {
          main.textContent = err.message;
        });
    });
  }
Copy the code

Matters needing attention

Do not abuse dynamic imports (only when necessary). A static framework initializes dependencies better, and makes it easier for static analysis tools and Tree shaking to work.

Different from static import:

  • import()It can be used from scripts, not just from modules.
  • ifimport()Used in modules, it can appear anywhere at any level and will not be promoted.
  • import()Accept arbitrary strings (the run-time determined template strings are shown here), not just static string literals.
  • Exists in modulesimport()Dependencies are not established and must be obtained and evaluated before the included modules can be evaluated.
  • import()Do not establish dependencies that can be statically analyzed (however, in cases such asimport('./foo.js')In such simple cases, the implementation may still be able to perform speculative fetching.

3, the import. Meta

An overview of the

Import. meta is an object that exposes context-specific metadata attributes to JavaScript modules. It contains information about the module, such as the URL of the module.

The proposal is currently in Stage 4.

TC39 express

motivation

Typically, the host environment can provide useful module-specific information for code evaluation within a module. Here are a few examples.

The URL or file name of the module

This is provided in the CommonJS module system of Node.js via the scoped __filename variable (and its corresponding __dirname). This allows for easy resolution of resources relative to module files through code, for example:

const fs = require("fs");
const path = require("path");
const bytes = fs.readFileSync(path.resolve(__dirname, "data.bin"));
Copy the code

If no _dirname is available, code like fs.readfilesync (‘data.bin’) will resolve data.bin relative to the current working directory. This is usually of no use to library authors because they tie their resources to modules that can be located anywhere associated with CWD.

Similar behavior exists in browsers, which replace file names with urls.

Scrpit tags import resources

<script data-option="value" src="library.js"></script>

// Get the value of data-option as follows
const theOption = document.currentScript.dataset.option;
Copy the code

The mechanism of using (valid) global variables instead of lexical scoped values for this is problematic because it means that the value is set only at the top level and must be saved there to be used in any asynchronous code.

mainModule

In Node.js, it is common to determine whether you are the main or entry module of your program by using the following code:

if (module === process.mainModule) {
  // run tests for this library, or provide a CLI interface, or similar
}
Copy the code

That is, it allows a single file to be used as a library when imported, or as a side-effect program when run directly.

Notice how this particular formula depends on comparing the host-supplied scope value module with the host-supplied global value process.mainModule.

Other conceivable circumstances

  • otherNode.jsMethods, such asmodule.childrenrequire.resolve()
  • About module OwnershippackageInformation, or throughNode.jspackage.jsonweb package
  • Point to embed inHTMLIn the moduleJavaScriptModule inclusionDocumentFragmentA pointer to the

grammar

import.meta

The import.meta object consists of a keyword import, a dot symbol, and a meta attribute name. Normally import. Is accessed as a context for an attribute, but import is not a real object here.

The import.meta object has a null prototype object. The object is extensible, and its properties are writable, configurable, and enumerable.

Example:

The following code uses two attributes we added to import.meta:

(async() = > {const response = await fetch(new URL(".. /hamsters.jpg".import.meta.url));
  const blob = await response.blob();

  const size = import.meta.scriptElement.dataset.size || 300;

  const image = new Image();
  image.src = URL.createObjectURL(blob);
  image.width = image.height = size;

  document.body.appendChild(image); }) ();Copy the code

When this module is loaded, regardless of its location, it loads the hamsters.jpg file and displays the image. The size of the image can be configured using the script element used to import it, for example:

<script type="module" src="path/to/hamster-displayer.mjs" data-size="500"></script>
Copy the code

4, BigInt,

An overview of the

In JavaScript, BigInt is a numeric type of data that can represent an integer in any precision format. (New base data type)

The proposal is currently in Stage 4.

TC39 express

motivation

In JavaScript, precision loss occurs when number is greater than number.max_safe_INTEGER.

const x = Number.MAX_SAFE_INTEGER;
// 9007199254740991, this is 1 less than 2^53

const y = x + 1;
// 9007199254740992, ok, checks out

const z = x + 2;
// 9007199254740992, wait, that’s the same as above!
Copy the code

BigInt’s proposal addresses this problem by making Number.max_safe_INTEGER no longer a JavaScript constraint. It can represent integers with arbitrary precision.

grammar

Convert a Number to BigInt by using the BigInt method or by adding the n suffix after a numeric value.

Example:

const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
// 9007199254740991n
const hugeButString = BigInt('9007199254740991');
// 9007199254740991n
Copy the code
Number.MAX_SAFE_INTEGER;
/ / 9007199254740991
Number.MAX_SAFE_INTEGER + 10 - 10;
// 9007199254740990 (precision lost)
BigInt(Number.MAX_SAFE_INTEGER) + 10n - 10n;
// 9007199254740991n (Calculated result is bigint)

typeof 9007199254740991n= = ="bigint";
// true
Copy the code

You can use the +, *, -, **, % operators, just as you would with Number.

const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER);
/ / 9007199254740991

const maxPlusOne = previousMaxSafe + 1n;
// 9007199254740992n
 
const theFuture = previousMaxSafe + 2n;
// 9007199254740993n, this works now!

const multi = previousMaxSafe * 2n;
// 18014398509481982n

constSubtr = multi -10n;
// 18014398509481972n

const mod = multi % 10n;
// 2n

const bigN = 2n六四事件54n;
// 18014398509481984n

bigN * -1n
N. / / - 18014398509481984
Copy the code

Matters needing attention

1. The/division operator can also be used, but will round down.

Because BigInt is not BigDecimal.

const expected = 4n / 2n; // 2n
const rounded = 5n / 2n;  / / 2 n, not 2.5 n
Copy the code

BigInt is not strictly equal to Number.

2n= = =2; // false
2n= =2;  // true
Copy the code

3. The comparison operator can be used normally.

1n < 2;  // true 
2n > 1;  // true 
2 > 2;   // false 
2n > 2;  // false 
2n> =2; // true
Copy the code

4, The array mixed BigInt and Number can be sorted properly.

const mixed = [4n.6, -12n.10.4.0.0n];
// [4n, 6, -12n, 10, 4, 0, 0n]

mixed.sort();
// [-12n, 0, 0n, 10, 4n, 4, 6]
Copy the code

BigInt cannot be mixed with Number.

1n + 2;
// TypeError: Cannot mix BigInt and other types, use explicit conversions

1n * 2;
// TypeError: Cannot mix BigInt and other types, use explicit conversions
Copy the code

5, Promise. AllSettled

An overview of the

The promise.allSettled () method returns a Promise after all the given promises have fulfilled or rejected, with an array of objects each representing the corresponding Promise result.

This is often used when you have multiple asynchronous tasks that don’t depend on each other to complete successfully, or when you always want to know the outcome of each promise.

Promise.all(), by contrast, is better suited to relying on each other or ending immediately in either reject.

The proposal is currently in Stage 4.

TC39 express

motivation

api describe
Promise.allSettled Not short circuit This proposal 🆕
Promise.all Short-circuit when there is an input value rejected In ES2015, add ✅
Promise.race Short circuit when there is stable input value In ES2015, add ✅
Promise.any A short circuit occurs when there is a depressing input value In ES2021, add ✅

As can be seen, promise. all, promise. race, and promise. any all have short circuit behavior.

Promise. AllSettled does not have short circuit behavior. It ensures that every Promise can be fulfilled, no matter the state is a pity or rejected.

grammar

Promise.allSettled(iterable)

  • 可迭代: an iterable object, for exampleArrayEvery member of the team isPromise.

Once each promise in the set of promises specified has been completed, unpromised promises will be completed asynchronously, whether successfully made or rejected. At that point, the returned promise processor will pass in as input an array of promises containing the results of each promise in the original Promises set.

For each result object, there is a status string. If its value is fulfilled, there is a value on the result object. If the value is rejected, there is a reason. Value (or Reason) reflects the value of each promise resolution (or rejection).

Example:

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) = > setTimeout(reject, 100.'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises)
  .then((results) = > results.forEach((result) = > console.log(result.status)));

// expected output:
// "fulfilled"
// "rejected"
Copy the code
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) = > reject('I'm Promise_1 to fail'));
const promise4 = new Promise((resolve, reject) = > reject('I'm Promise_2 to fail'));
const promiseList = [promise1, promise2, promise3, promise4];
Promise.allSettled(promiseList)
.then(values= > {
  console.log(values);
});
Copy the code

6, globalThis

An overview of the

The global attribute globalThis contains the global this value, similar to a global object.

The proposal is currently in Stage 4.

TC39 express

motivation

It is difficult to write portable ECMAScript code that accesses global objects. On the Web side, it could be Window or self or this or frames. In Node.js, it’s global or this.

Of these, only this is available in shells such as V8 D8 or JavaScriptCore JSC. This also works in a standalone function call in sloppy mode, but is undefined in a module or in strict mode within a function.

In this context, global objects are still accessible through Function(‘return this’)(), but not in some CSP Settings, such as in Chrome applications.

Here is some code to get a global object from the outside and pass it to IIFE as a single argument, which works in most cases, but doesn’t actually work with D8 when in strict mode within modules or functions (which can be fixed using function tricks) :

function foo() {
  // If we're in a browser, the global namespace is named 'window'. If we're
  // in node, it's named 'global'. If we're in a shell, 'this' might work.
  (typeof window! = ="undefined"
    ? window
    : (typeof process === 'object' &&
      typeof require= = ='function' &&
      typeof global= = ='object')?global
      : this);
}
Copy the code

In addition, due to CSP problems, ES6-shim must convert from Function(‘return this’)(). Currently browsers handle node, Web workers, and frames as follows:

const getGlobal = function () {
  // the only reliable means to get the global object is
  // `Function('return this')()`
  // However, this causes CSP violations in Chrome apps.
  if (typeofself ! = ='undefined') { return self; }
  if (typeof window! = ='undefined') { return window; }
  if (typeof global! = ='undefined') { return global; }
  throw new Error('unable to locate global object');
};
Copy the code

grammar

In browsers, window, globalThis, and this are all equal:

window= = =this;       // true
window === globalThis; // true
this === globalThis;   // true
Copy the code

In Node.js, global, globalThis, and this are all equal:

> global= = =this;       // true
> global === globalThis; // true
> this === globalThis;   // true
Copy the code

7, Nullish Coalescing

An overview of the

Null-value merge operator (??) Is a logical operator that returns the right-hand operand if the left-hand operand is null or undefined, otherwise returns the left-hand operand.

The proposal is currently in Stage 4.

TC39 express

motivation

When performing property access, if the result of property access is null or undefined, you usually need to provide a default value. At present, in JavaScript to express the intention of a typical way is to use the | | operator.

const response = {
  settings: {
    nullValue: null.height: 400.animationDuration: 0.headerText: ' '.showSplashScreen: false,}};const undefinedValue = response.settings.undefinedValue || 'some other default';
// result: 'some other default'
const nullValue = response.settings.nullValue || 'some other default';
// result: 'some other default'
Copy the code

This works well for the common case of null and undefined values, but there are many falsy values that can produce surprising results:

const headerText = response.settings.headerText || 'Hello, world! ';
// Potentially unintended. '' is falsy, result: 'Hello, world! '
const animationDuration = response.settings.animationDuration || 300;
// Potentially unintended. 0 is falsy, result: 300
const showSplashScreen = response.settings.showSplashScreen || true;
// Potentially unintended. false is falsy, result: true
Copy the code

grammar

leftExpr ?? rightExpr

Example:

let user = {
  u10.u2false.u3null.u4undefined.u5' ',}let u2 = user.u2 ?? Users' 2 '// false
let u3 = user.u3 ?? Users' 3 '3 / / users
let u4 = user.u4 ?? Users' 4 '4 / / users
let u5 = user.u5 ?? Users' 5 '/ /"
Copy the code

8, Optional Chaining

An overview of the

The optional chain operator (? .). Allows you to read the value of a property located deep in the chain of connected objects without explicitly validating that each reference in the chain is valid. ? The. Operator functions like the. Chain operator, except that it does not cause an error in the case of a reference (null or undefined), and the expression shorts out the return value of undefined. When used with a function call, returns undefined if the given function does not exist.

The proposal is currently in Stage 4.

TC39 express

motivation

When looking for attribute values deep in the object structure, it is usually necessary to check for the presence of intermediate nodes:

const street = user.address && user.address.street;
Copy the code

In addition, many apis return objects or NULL /undefined, and may want to extract attributes from the result only if the result is not NULL:

const fooInput = myForm.querySelector('input[name=foo]');
const fooValue = fooInput ? fooInput.value : undefined;
Copy the code

grammar

obj? .prop

obj? .[expr]

arr? .[index]

func? .(args)

Example:

Access object properties using optional chains:

let user = {};
let u1 = user.childer.name;
// TypeError: Cannot read property 'name' of undefined
letu1 = user? .childer? .name;// undefined
Copy the code

Function calls are made using optional chains

function doSomething(onContent, onError) {
  try {
   // ... do something with the data
  }
  catch(err) { onError? .(err.message);// If onError is undefined, there will be no exception}}Copy the code

When accessing attributes using square brackets and attribute names, you can also use the optional chain operator:

constnestedProp = obj? .'prop' + 'Name'];
Copy the code

Optional chain access to array elements:

letarrayItem = arr? .42];
Copy the code

ES12 (ES2021)

1, the String. The prototype. The replaceAll

An overview of the

The replaceAll() method returns a new string, all parts of which meet pattern are replaced by replacement. Pattern can be a string or a RegExp, replacement can be a string or a function that is called each time a match is made.

The proposal is currently in Stage 4.

TC39 express

motivation

There is currently no way to replace all instances of a substring of a string without using a global regular expression. String.prototype.replace replaces the first occurrence of a substring only when used with String arguments.

By far the most common way to achieve this is to use global regular expressions:

const queryString = 'q=query+string+parameters';
const withSpaces = queryString.replace(/+/g.' ');
Copy the code

The downside of this approach is that special RegExp characters need to be escaped — note the escaped +.

Another solution is to combine string.split and array.join:

const queryString = 'q=query+string+parameters';
const withSpaces = queryString.split('+').join(' ');
Copy the code

This approach avoids any escape, but incurs the overhead of breaking the string into an array and then gluing the array items back together again.

grammar

String.prototype.replaceAll(searchValue, replaceValue);

Returns a brand new string, with all characters matching the rules replaced.

In addition to the following two situations, in all other cases the String. The prototype. The replaceAll behavior are linked to a String. The prototype. Replace the same:

  • ifsearchValueIs a string,String.prototype.replaceOnly replace what occurs oncesearchValueAnd theString.prototype.replaceAllReplace all occurrencessearchValue.
  • ifsearchValueIs a non-global regular expressionString.prototype.replaceReplaces a single match, whileString.prototype.replaceAllThrow an exception. This is done to avoid the inherent confusion between the lack of a global flag (which means “do not replace all”) and the name of the method being called (” Replace all “is strongly recommended).

Example:

const queryString = 'q=query+string+parameters'; 
const withSpaces = queryString.replaceAll('+'.' ');
Copy the code

2, Promise. Any)

An overview of the

Promise.any() receives a Promise iterable and returns the Promise that succeeded as soon as one of the promises succeeds. If none of the iterable promises succeeds (i.e. all promises fail/reject), return a failed promise.

The proposal is currently in Stage 4.

TC39 express

grammar

Promise.any(iterable)

  • Iterable: An iterable object, such as Array.

Example:

const promise1 = new Promise((resolve, reject) = > reject('failed Promise1'));
const promise2 = new Promise((resolve, reject) = > reject('Promise2 fails'));
const promiseList = [promise1, promise2];
Promise.any(promiseList)
.then(values= >{
  console.log(values);
})
.catch(error= >{
  console.log(error);
});
Copy the code

In the example above, Error is AggregateError, which is a new error subclass that groups the various errors together. Each instance of AggregateError contains a pointer to an exception array.

Logical Assignment Operators

An overview of the

Logical assignment operator

The proposal is currently in Stage 4.

TC39 express

grammar

| | =

&& =

?? =

Example:

a ||= b;
/ / equivalent to the
a = a || (a = b);

a &&= b;
/ / equivalent to thea = a && (a = b); a ?? = b;/ / equivalent to the
a = a ?? (a = b);
Copy the code

4, Numeric Separators

An overview of the

Numeric separator

It is the result of merging its earlier draft with Christophe Porteneuve’s proposal numeric Underscores, to extend the existing NumericLiteral to allow separators between numbers.

The proposal is currently in Stage 4.

TC39 express

motivation

This feature enables developers to make their numeric text more readable by creating visual separations between numbers.

grammar

Use the underscore _ (U+005F) to separate numbers.

Example:

The decimal system

const price1 = 1 _000_000_000;
const price2 = 1000000000;
price1 === price2; // true

const float1 = 0.000 _001;
const float2 = 0.000001;
float1 === float2; // true;
Copy the code

binary

const nibbles1 = 0b1010_0001_1000_0101;
const nibbles2 = 0b1010000110000101;
nibbles1 === nibbles2; // true
Copy the code

hexadecimal

const message1 = 0xA1_B1_C1;
const message2 = 0xA1B1C1;
message1 === message2; // true
Copy the code

BigInt

const big1 = 1_000_000_000n;
const big2 = 1000000000n;
big1 === big2; // true
Copy the code