preface


TC39, the body responsible for developing the ECMAScript standard, has released new features every year since THE release of ES6.

ES2020 will also be released this year. I searched a circle of relevant articles on the Internet, but either the content is incomplete or the source is not indicated, and the official documents are not easy to read in pure English. As a result of this article, the new features in this article are from the closed proposal document on Github.

The good news is that many of the new ES2020 features are already available in Chrome, and in this article, we’ll cover them in detail and see the logic behind the new features.

An overview of new features

  1. Optional Chaining
  2. Nullish coalescing Operator
  3. globalThis
  4. Promise.allSettled
  5. String.prototype.matchAll
  6. BigInt
  7. for-in mechanics
  8. import()

Detailed introduction

Optional chain operator – Optional Chaining

Sometimes in order to access deeply nested attributes, we need to write a long && chain to check if each attribute exists, like this:


var price = result && result.body && result.body.data && result.body.data.price;Copy the code


In fact, we often do this in everyday development, and if we don’t do it, it can lead to exceptions. To simplify the above code, an optional chain operator was added as follows:


var price = result? .body? .data? .price;Copy the code


Optional operators can apply to methods as well as properties.


const result = {}; const price = result? .body? .data? .price; const user = {}; const name = user? .getInfo? .().getName? . (); const respone = {}; const firstProduct = respone? .body? .data? .products? . [0];Copy the code


Parameter Description Value Nullish coalescing Operator

When retrieving an object attribute, we often need to set the default value for the null/undefined attribute. The normal situation, we can use the | | operator, such as:


var user = {};
var name = user.name || 'p9';Copy the code


However, doing so would result in false, 0, empty string, and so on being overwritten, which is undesirable and can be avoided by using the null-value merge operator. Such as:


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'
const headerText = response.settings.headerText ?? 'Hello, world! '; // result: ' '
const animationDuration = response.settings.animationDuration ?? 300; // result: 0
const showSplashScreen = response.settings.showSplashScreen ?? true; // result: falseCopy the code

Standardized global object – globalThis

It is difficult to easily get global objects in different environments. Because, on the browser side, the global object could be window or self or this or frames; In Node, global objects can be retrieved from either global or this; In non-strict mode, this in a method refers to a global object, but in strict mode it is undefined; Of course, we can get global objects with Function(‘return this’)(), but it is not accessible in some browsers with CSP Settings, such as Chrome Apps. These problems lead to the following code:


var getGlobal = function () {
    // the only reliable means to get the global object is
    // `Function('return this')()`
    // However, this causes CSP violations in Chrome apps.
    if(typeof self ! = ='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


So we need a standard way to get global objects in different environments: globalThis. Such as:


function canMakeHTTPRequest() {
    return typeof globalThis.XMLHttpRequest === 'function';
}

console.log(canMakeHTTPRequest());
// expected output (in a browser): true
Copy the code

Promise.allSettled

Promise. All can execute multiple tasks concurrently, but the drawback of Promise. All is that if one of the tasks fails, it goes straight into the catch process.


const promise1 = new Promise((resolve) => setTimeout(resolve, 200, 'good'));
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'reject'));
const promises = [promise1, promise2];

Promise.all(promises).
then((ret) = > {/ / not trigger the debugger}). The catch ((ret) = > {the console. The log (ret); / / reject}). The finally (() = > {/ / trigger, can't get the content we want});Copy the code


We need a way to ensure that if one task fails, other tasks will continue to execute to maximize business availability

. AllSettled will return a Promise. The returned Promise will be triggered after all the input promises resolve or reject.


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)));

// expected output:
// {status: "fulfilled", value: 3}
// {status: "rejected", reason: "foo"}
Copy the code


String.prototype.matchAll

MatchAll behaves a bit like match, but matchAll returns an iterator through which we can quickly get all the matching content.

Let’s look at what’s wrong with match before we get to matchAll. First let’s take a look at the code:


const regex = /t(e)(st(\d?) )/g; const string ='test1test2'; const results = string.match(regex); console.log(results); / / - > ['test1'.'test2']Copy the code


Test1 and test2 are matched. When you remove the global match option from the re, you get:


['test1'.'e'.'st1'.'2', index: 0, input: 'test1test2', groups: undefined]Copy the code


If you need a test2-related match, and you don’t have one, how do you get a test2-related match? Before that, we can use replace, which is a bit longer:


var regex = /t(e)(st(\d?) )/g; var string ='test1test2';
var matches = [];
string.replace(regex, function () {
    var match = Array.prototype.slice.call(arguments, 0, -2);
    match.input = arguments[arguments.length - 1];
    match.index = arguments[arguments.length - 2];
    matches.push(match);
    // example: ['test1'.'e'.'st1'.'1'] with properties `index` and `input` }); matches; / / /"test1"."e"."st1"."1", input: "test1test2", index: 0]
// ["test2"."e"."st2"."2", input: "test1test2", index: 5]
Copy the code


To simplify the smelly, long code above, matchAll was born


letregexp = /t(e)(st(\d?) )/g;let str = 'test1test2';

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

console.log(array[0]);
// expected output: Array ["test1"."e"."st1"."1"]

console.log(array[1]);
// expected output: Array ["test2"."e"."st2"."2"]Copy the code


BigInt

BigInt is a built-in object that provides a way to represent integers greater than 253-1. This was originally the largest Number that could be represented as Number in Javascript.

The syntax is as follows: We can create a BigInt variable by adding n after a number or by using BigInt().


const theBiggestInt = 9007199254740991n;

const alsoHuge = BigInt(9007199254740991);
// 9007199254740991n

typeof alsoHuge === 'bigint' // trueCopy the code


Bigint, like number, supports +, *, -, **, and % operators:


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 const subtr = multi -- 10n; // ↪ 18014398509481972n const mod = multi % 10n; // ↪ 2n const bigN = 2n ** 54n; // ↪ 18014398509481984n bigN * -1n // ↪ -- 18014398509481984nCopy the code


Bigint can be compared to number as usual:


1n < 2
// ↪ true

2n > 1
// ↪ true

2 > 2
// ↪ false

2n > 2
// ↪ false

2n >= 2
// ↪ trueCopy the code


When converted to a Boolean, bigint behaves like number:


if (0n) {
  console.log('Hello from the if! ');
} else {
  console.log('Hello from the else! '); } / / ↪"Hello from the else!"0 n | | 12 n / / ↪ 12 n 0 n && 12 n / / ↪ 0 n Boolean (0, n) / / ↪false/ / ↪ Boolean (n)true! 12 n / / ↪false! 0nCopy the code


Bigint can be converted to a string just like number


1n + '2'/ / ↪"12"

'2'N + 1 / / ↪"21"Copy the code


Points to be aware of

Bigint is not exactly equal to number

0n === 0
// ↪ false

0n == 0
// ↪ trueCopy the code


Bigint cannot be computed 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 conversionsCopy the code


Bigint cannot be converted to number using +


+1n // ↪ TypeError: Cannot convert a BigInt value to a number number (1n) // ↪ 1Copy the code


for-in mechanics

The order in which the contents of the for in loop would have been different in different engines is now standardized.

Asynchronously loading modules – import()

Import () is used when modules need to be dynamically loaded

// logger.js
export default {
    log: function() {
        console.log(...arguments);
    }
};

import('./logger.js')
  .then((module)=>{
  module.default.log('p9');
})Copy the code


Of course, you can also wait with await

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

Afterword.

It’s better to teach people how to fish than to teach them how to fish. Check out the documentation on GitHub for the new ES features in the future.


The resources

MDN

Github.com/tc39/propos…

Developers.google.com/web/updates…

Blog.logrocket.com/5-es2019-fe…