The new version of ECMAScript feature analysis

The new version of ECMAScript feature analysis

1 Guide to this article

JavaScript is a language that supports multiple programming paradigms, with high flexibility and powerful functions. Because of its flexibility, JavaScript has many ways to use it, and it is applied to various scenarios such as browsers, client software, and embedded hardware. Nowadays, when the programming paradigm of object-oriented (OOP) is popular, this diversified language has a variety of programming paradigms and uses different forms in each scenario. There are big differences in thinking between users and different opinions. Therefore, It was extremely misunderstood. It is precise because of this relative complexity that front-end engineers need to study and research this language more systematically and in-depth. Now, JavaScript is being iteratively updated at an accelerated pace under the impetus of TC39 (TC39 is composed of all parties including browser manufacturers, they meet to promote the proposal of JavaScript features to advance along a strict process), and some new features will appear every year. . It is necessary for front-end engineers to understand these new features in order to better exert their energy in front-line Internet companies.

1.1 The relationship between JavaScript and ECMAScript 6

JavaScript originated from LiveScript, a scripting language created by Netscape in 1995 for its own browser. Later, because of the rise of Sun’s Java language, Netscape changed the name of LiveScript to JavaScript in order to gain popularity, but in fact, Java and JavaScript have nothing to do with each other. In 1997, Netscape submitted the JavaScript specification to Ecma International (ecma-international.org, referred to as EI), and the first version of ECMAScript (abbreviated as "ES") was born. Then in 1999 was born the very stable ES 3, which is the most widely used version by front-end engineers before December 2009.

ES 5 was born in December 2009. You may ask ES 4. Sorry, because version 4 is too radical, the opinions of EI committee members are different. In the end, it is still dead, and the smoother and gentler version 5 inherits some of the new features and functions of version 4. EI released ES 6 in June 2015, which is the version we use widely today. After that, a minimally updated version of ES is released every June. For example, the ES 2016 released in June 2016 is called ES 7, and this version only adds two new features. As of this writing, the ES 11 proposal has been finalized. In summary, JavaScript is an implementation of the ECMAScript specification. In order to facilitate readers' understanding and communication, and because the changes after ECMAScript 6 are not particularly large, if there is no special description, the JavaScript corresponding to the specification described later in this book defaults to ES 6. The new features of ES 6 have been highlighted in the "1.2" section, and the new features of ES 7~11 will be introduced in this article.

2. ES 7 new features

Array.prototype.includes

includes is a very useful method on Array, which is used to quickly find whether an element is contained in the array, including NaN (so it is not the same as indexOf).

(() => {
let arr = [1, 2, 3, NaN];
if (arr.includes(2)) {
//Find if 2 exists in the arr array
console.log("Found it!"); //>> Found it!
}
if (!arr.includes(2, 3)) {
//The second parameter 3 represents the item whose array index is 3, that is, the fourth item starts to search
console.warn("does not exist!"); //>> does not exist!
}
//The following two sentences explain the difference between incluedes and indexOf
console.log(arr.includes(NaN)); //true
console.log(arr.indexOf(NaN) != -1); //false
})();

Infix notation of exponential function

This is a feature related to Math. pow. Remember the writing of i++, x += x? The infix notation of exponential function is similar. Like the python language, JavaScript also uses two-star symbols ** to represent Math. pow. There are two benefits: a. The infix notation is more concise than the function notation, which makes it more desirable. b. Facilitate calculations in the fields of mathematics, physics, robotics, etc.

//Usage 1: x ** y
let squared = 2 ** 2; //is equivalent to: 2 * 2
let cubed = 2 ** 3; //is equivalent to: 2 * 2 * 2
//Usage 2: x **= y
let a = 2;
a **= 2;//Same as: a = a * a;
let b = 3;
b **= 3;//Same as: b = b * b * b;

Yes, it is a very sweet piece of syntactic sugar (refers to a certain grammar added to a computer language, which has no effect on the function of the language but is more convenient for programmers to use).

3. ES 8 new features

Object.values() / Object.entries The usage is Object.values(obj), obj can be an object or an array.

const obj = {x:'xxxx', y: 1 };

Object.values(obj); // ['xxxx', 1]
const obj = ['e','s', '8']; // equivalent to {0:'e', 1:'s', 2: '8' };
Object.values(obj); // ['e','s', '8']

//When the number is used as the key of the object, the returned array is sorted in ascending order of the key
const obj = {9:'xxx', 2:'yyy', 5:'zzz' };
Object.values(obj); // ['yyy','zzz','xxx']
Object.values('es8'); // ['e','s', '8']

The Object.entries method returns an array [key, value] of the enumerable property values ​​of a given object, similar to Object.values.

const obj = { x: 'xxx', y: 1 };
Object.entries(obj); // [['x', 'xxx'], ['y', 1]]
const obj = ['e', 's', '8'];
Object.entries(obj); // [['0', 'e'], ['1', 's'], ['2', '8']]
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.entries(obj); // [['1', 'yyy'], ['3', 'zzz'], ['10', 'xxx']]
Object.entries('es8'); // [['0', 'e'], ['1', 's'], ['2', '8']]

String append In ES 8 String has added two new instance functions String.prototype.padStart and String.prototype.padEnd, allowing empty strings or other strings to be added to the beginning or end of the original string.

  • String.padStart(targetLength,[padString]) targetLength: The target length to which the current string needs to be filled. If this 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 will be kept, and the other parts will be truncated. The default value of this parameter is a space.

  • String.padEnd(targetLength,padString]) The parameter definition is the same as above.

'es8'.padStart(2);          // 'es8'
'es8'.padStart(5);          // '  es8'
'es8'.padStart(6, '1989');  // '198es9'
'es8'.padStart(14, 'coffe');  // 'coffecoffeces8'
'es8'.padStart(7, '0');     // '0000es8'

'es8'.padEnd(2);            // 'es8'
'es8'.padEnd(5);            // 'es8  '
'es8'.padEnd(6, '1989');    // 'es8989'
'es8'.padEnd(14, 'coffe');    // 'es8coffecoffec'
'es8'.padEnd(7, '9');       // 'es89999'

Object.getOwnPropertyDescriptors

The getOwnPropertyDescriptors method returns all the property content of a specified object, and the property content is only directly defined by itself, rather than inherited from the prototype of the object. The definition is: Object.getOwnPropertyDescriptors(obj), obj refers to the target object, the value returned by this method may be configurable, enumerable, writable, get, set and value.

const obj = { 
  get es7() { return 7; },
  get es8() { return 8; }
};
Object.getOwnPropertyDescriptors(obj);
// {
//   es7: {
//     configurable: true,
//     enumerable: true,
//     get: function es7(){}, //the getter function
//     set: undefined
//   },
//   es8: {
//     configurable: true,
//     enumerable: true,
//     get: function es8(){}, //the getter function
//     set: undefined
//   }
// }

Asynchronous function Async Functions is what we often call Async/Await. I believe everyone is familiar with this concept. Async/Await is a syntactic sugar for processing JS asynchronous operations, which can help us get rid of callback hell and write more elegant code. In popular understanding, the role of the async keyword is to tell the compiler to treat the calibrated function differently. When the compiler encounters the await keyword in the calibrated function, it should temporarily stop running and wait until the await calibrated function is processed before performing the corresponding operation. If the function is fulfilled, the return value is the fulfillment value, otherwise the rejection value is obtained. The following is a good understanding by comparing the common promise writing method:

async function asyncFunc() {
const result = await otherAsyncFunc();// otherAsyncFunc() returns a Promise object
console.log(result);
}
// Equivalent to:
function asyncFunc() {
return otherAsyncFunc()// otherAsyncFunc() returns a Promise object
.then(result => {
console.log(result);
});
}

The advantage is more obvious when processing multiple asynchronous functions in sequence:

async function asyncFunc() {
const result1 = await otherAsyncFunc1();// otherAsyncFunc1() returns a Promise object
console.log(result1);
const result2 = await otherAsyncFunc2();// otherAsyncFunc2() returns a Promise object
console.log(result2);
}

// Equivalent to:
function asyncFunc() {
return otherAsyncFunc1()// otherAsyncFunc1() returns a Promise object
.then(result1 => {
console.log(result1);
return otherAsyncFunc2();// otherAsyncFunc2() returns a Promise object
})
.then(result2 => {
console.log(result2);
});
}

Process multiple asynchronous functions in parallel:

async function asyncFunc() {
const [result1, result2] = await Promise.all([
otherAsyncFunc1(),// otherAsyncFunc1() returns a Promise object
otherAsyncFunc2() // otherAsyncFunc2() returns a Promise object
]);
console.log(result1, result2);
}
// Equivalent to:
function asyncFunc() {
return Promise.all([
otherAsyncFunc1(),// otherAsyncFunc1() returns a Promise object
otherAsyncFunc2() // otherAsyncFunc2() returns a Promise object
])
.then([result1, result2] => {
console.log(result1, result2);
});
}

Handling errors:

async function asyncFunc() {
try {
await otherAsyncFunc();// otherAsyncFunc() returns a Promise object
} catch (err) {
console.error(err);
}
}
// Equivalent to:
function asyncFunc() {
return otherAsyncFunc()// otherAsyncFunc() returns a Promise object
.catch(err => {
console.error(err);
});
}

Async Functions can take up a lot of space if you want to expand it. Since this article is an introductory article, it will not be discussed in depth.

ES 9 new features

Asynchronous iterator

At certain moments of async/await, you may try to call an asynchronous function in a synchronous loop. E.g:

async function func(array) {
  for (let i of array) {
    await someFunc(i);
  }
}

This code will not achieve the intended purpose, nor will the following paragraph:

async function func(array) {
  array.forEach(async i => {
    await someFunc(i);
  });
}

In the above code, the loop itself remains synchronized, and all calls are completed before the internal asynchronous function. After the introduction of asynchronous iterators, they are just like regular iterators, except that the next() method returns a Promise. So await can be used with for...of loops to run asynchronous operations in a serial manner.

async function func(array) {
  for await (let i of array) {// Asynchronous iteration
    someFunc(i);
  }
}

Promise.prototype.finally

A Promise call chain either successfully reaches the last .then(), or fails to trigger .catch(). In some cases, you want to run the same code regardless of the success or failure of the Promise, such as clearing the array, deleting the dialog, closing the database connection, etc. .finally() allows for this purpose.

function func() {
promiseFunc() //Return a Promise object
.then(() => {})
.then(() => {})
.catch(err => {
console.log(err);
})
.finally(() => {
//Regardless of whether promiseFunc() runs successfully or fails, the code here will be called
});
}

Revised the literal escape Before ES9, \u means unicode escape, \x means hexadecimal escape, \ followed by a number means octal escape, which makes it impossible to create a specific string, such as Windows file path C:\uuu\ xxx\111. To remove the syntax restriction of escape sequences, you can use the markup function String.raw before the template string.

let s = `\u{54}` //will be escaped into unicode "T"
console.log(s);//>> T
let str = String.raw`\u{54}`; // will not be escaped
console.log(str);//>> \u{54}

Rest / Spread

This is what we usually call the three points... This feature has been introduced in ES6, but the role of objects in ES6 is limited to arrays. In ES9, rest parameters and spread operators like arrays are provided for objects:

const obj = {
a: 1,
b: 2,
c: 3
};
const {a, ...param} = obj; //here... is rest
console.log(a); //>> 1
console.log(param); //>> {b: 2, c: 3}
function foo({ a, ...param }) {//here... still rest
console.log(a); //>> 1
console.log(param); //>> {b: 2, c: 3}
}
const param = {b: 2, c: 3 };
foo({ a: 1, ...param }); //here...is spread

Regular expression dotAll mode The midpoint of the regular expression. Matches any single character except carriage return. The marks changes this behavior, allowing carriage return and line feed to be matched.

/hello.world/.test('hello\nworld');  // false
/hello.world/s.test('hello\nworld'); // true
console.log(/hello.world/s.test(`hello
world`))   //>> true

Regular expression named capture group

After using exec() to match in Javascript regular expressions, it can return an array-like object containing the matched string.

const reDate = /(\d{4})-(\d{2})-(\d{2})/,
match = reDate.exec("2018-08-06");
console.log(match);//>> [2018-08-06, 2018, 08, 06]

//In this way, you can directly use the index to get the year, month, and day:
let year = match[1]; //>> 2018
let month = match[2]; //>> 08
let day = match[3]; //>> 06

The 0th item of the returned array is the text that matches the regular expression, the first item is the text that matches the first group \d{4} of reDate (if any), and the second item is the text that matches the reDate The text matching the second group \d{2} (if any), and so on. Groups of regular expressions are wrapped in (). In the above case, if the date format is changed to month, day and year, then after changing the structure of the regular expression, the code of the variable assignment part may also be changed. The following example:

 const reDate = /(\d{2})-(\d{2})-(\d{4})/,//The expression structure has changed
match = reDate.exec("08-06-2018");
console.log(match);//>> [08-06-2018, 08, 06, 2018]

//At this time, the assignment code of year, month, and day has to be changed too, there are so many places to change! what to do?
let year = match[3]; //>> 2018
let month = match[1]; //>> 08
let day = match[2]; //>> 06

It can be found that there are too many changes in the above wording. Is there any way to change the code less and save trouble? have! ES9 allows the use of the symbol ? to name the capture group (that is, the "matched group"), an example is as follows:

const reDate = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
match = reDate.exec("2018-08-06");
console.log(match);
//>> [2018-08-06, 08, 06, 2018, groups: {day: 06, month: 08, year: 2018}]

//At this time, use the groups object to get the year, month, and day. No matter how the regular expression is changed, the following three lines do not need to be changed, which saves trouble!
let year = match.groups.year; //>> 2018
let month = match.groups.month; //>> 08
let day = match.groups.day; //>> 06

The way to write a named capture group is to define a name for each capture group, and then store it in the groups attribute of the return value.

Assertions after regular expressions

Let's take a look at an example of regular expression preemptive assertion:

const re1 = /\D(\d+)/,
re2 = /\D(?=\d+)/,//"?=" is a positive lookahead assertion
match1 = re1.exec("$123.45"),
match2 = re2.exec("$123.45");
console.log(match1[0]); //>> $123
console.log(match2[0]); //>> $

In ES9, you can use ?<= to make positive and backward assertions, and you can get the price of the currency without ignoring the currency symbol.

const re= /(?<=\D)[\d\.]+/,
    match = re.exec("$123.45");
console.log(match[0]); //>> 123.45

The above forward-backward assertion means that the condition of \D must be met, but the characters matched by \D will not be output as a result (because the preceding/following assertion actually only matches one position). If it is like this:

const re= /(?<=\D)[\d\.]+/,
    match1 = re.exec("123.45"),
    match2 = re.exec("12345");
console.log(match1 && match1[0]); //>> 45
console.log(match2 && match1[0]); //>> null

It can be seen that match1 matches 45. This is because there is no matching content that matches \D before. It will always find content that matches \D, that is, and then return to the following content. If match2 does not meet the conditions for the positive reverse assertion, the result will be null.

Regular expression Unicode escape

Before ES9, local access to Unicode character attributes in regular expressions was not allowed. ES9 adds Unicode attribute escapes in the form of \p{...} and \P{...}, using the mark u (unicode) setting in the regular expression, in the {...} of \p, You can use key-value pairs to set the attributes that need to be matched instead of specific content.

const regex = /\p{Script=Greek}/u;//Greek
console.log(regex.test('a')); //>> flase
console.log(regex.test('Σ')); //>> true

ES 10 new features

Optional catch variable binding

Before ES10, we had to bind exception variables to catch clauses through syntax, whether necessary or not. Many times the catch block is redundant, and ES10 allows us to simply omit variables.

//Before it was
try {} catch(e) {}

//It can be written after ES10
try {} catch {}//The variable e is omitted

What is a superset of JSON? Simply put, JSON is a subset of ECMAScript, which means that ECMAScript is compatible with all the text supported by JSON content. ECMAScript clarifies in the standard JSON.parse that JSON is indeed a subset, but since the content of JSON can normally contain U+2028 line separators and U+2029 paragraph separators, ECMAScript cannot. Therefore, the draft aims to Solve this problem. Before that, if you used JSON.parse() to execute a string with the above special characters, you would only receive a SyntaxError error message. The draft is also backward compatible, and its only impact on users is to keep it as it is, that is, keep reporting SyntaxError in an operating environment that does not currently support special character parsing.

The description property of the Symbol object

ES10 added a read-only attribute description to the Symbol object, which returns a string containing the description of the Symbol. Add a description to the Symbol when it is created, and you can directly access the description, which is very useful for debugging.

let sym = Symbol('foo');//The added description content is "foo"
console.log(sym.description);//>> foo
sym = Symbol();
console.log(sym.description);//>> undefined
//Different from Symbol(), the symbol created with Symbol.for() method will be put into a global
//symbol in the registry. Symbol.for() does not create a new symbol every time, it will first check
//Check whether the given key is already in the registry. If it is, it will directly return to the one stored last time. Otherwise, it
//Will create another one.
sym = Symbol.for('bar');
console.log(sym.description);//>> bar

Revise Function.prototype.toString

The method toString() on the function prototype now returns exact characters, including spaces and comments.

function /* comment */ foo /* another comment */() {}
//The comment part will not be printed before ES10
console.log(foo.toString()); //>> function foo(){}
//In ES10, the comments will be printed together
console.log(foo.toString()); //>> function /* comment */ foo /* another comment */ (){}
//Notice:
//Arrow function is an exception
const bar /* comment */ = /* another comment */ () => {};
console.log(bar.toString()); //>> () => {}

Object.fromEntries In JavaScript operations, it is easy to convert data between various data structures, such as Map to Array, Map to Set, Object to Map and so on.

let map = new Map().set('foo', true).set('bar', false);
let arr = Array.from(map);
let set = new Set(map.values());
let obj = {foo: true, bar: false };
//The next sentence Object.entries() method returns the key-value pair array of the enumerable properties of the given object obj itself,
//Form like: [["foo",true],["bar",false]]
let newMap = new Map(Object.entries(obj));

But if we need to convert a list of key-value pairs into objects, we have to write some strenuous code.

let map = new Map().set("foo", true).set("bar", false);
let obj = Array.from(map).reduce((acc, [key, val]) => {
  return Object.assign(acc, {
    [key]: val
  });
}, {});

The purpose of this feature is to add a new static method Object.fromEntries to the object, which is used to convert a list of key-value pairs (such as Map, array, etc.) into an object. The conversion logic in the code of the previous block, now we only need one line of code to get it done.

const map = new Map().set("foo", true).set("bar", false);
let obj = Object.fromEntries(map);

More friendly JSON.stringify Before ES10, when you use JSON.stringify() to process characters that cannot be represented by UTF-8 encoding (U+D800 to U+DFFF), the returned result will be a garbled Unicode character "�". This feature proposes to use JSON.stringify() to safely represent these abnormal UTF-8 characters.

let r;
r = JSON.stringify("❤"); //Normal UTF-8 characters are output as they are
console.log(r); //>> "❤"
r = JSON.stringify('\u2764'); //Normal UTF-8 character encoding, the output will look like after decoding
console.log(r); //>> "❤"
r = JSON.stringify("\uDF06\uD834"); //unusual UTF-8 character encoding, output in unicode
console.log(r); //>> "\udf06\ud834"
r = JSON.stringify("\uDEAD"); //Unusual UTF-8 character encoding, output in unicode
console.log(r); //>> "\udead"

String.prototype. {TrimStart, trimEnd}

Added the trimStart() method and trimEnd() method of String. These two methods are easy to understand. The blank characters at the beginning and the end of the string are removed respectively, so there is no example to occupy the space.

Array.prototype.{flat,flatMap} This feature creates two new methods, among them:

  • All items of the Array.prototype.flat array will be reduced (flattened) by the specified dimensions, and then returned as a new array;

  • Array.prototype.flatMap will first execute the map() method once, and then flatten the array through a similar flat() method. It is equivalent to executing the flat() method again after executing map(), so when you execute map() if the result returned is an array, and then you want to flatten it, this method is convenient.

Let's look at a few examples to explain. First, the flat() method supports the flattening of multi-dimensional arrays, where Infinity can flatten multi-dimensional arrays into one-dimensional arrays.

let r;
r = ["1", ["9", ["8", ["9"]]]].flat();//4-dimensional array, the default dimension is reduced by 1, it becomes a 3-dimensional array
console.log(r); //>> ['1', '9', ['8', ['9']]]
r = ["1", ["9", ["8", ["9"]]]].flat(2); //4-dimensional array, reduced by 2, becomes a 2-dimensional array
console.log(r); //>> ['1', '9', '8', ['9']]
r = ["1", ["9", ["8", ["9"]]]].flat(Infinity);//4-dimensional array, up to a 1-dimensional array
console.log(r); //>> ['1', '9', '8', '9']

Then let's take a look at flatMap()

let r;
r = ["I love", "coffe 1989"].map(item => item.split(" "));
console.log(r); //>> [ [ 'I', 'love' ], [ 'coffe', '1989' ] ]

r = ["I love", "coffe 1989"].flatMap(item => item.split(" "));
console.log(r); //>>[ 'I', 'love', 'coffe', '1989' ]

ES 2020 (ES11) new features

Because it is too "new", if you want to use the new features of ES11, you need to install the babel plugin

plugins: [
    "@babel/plugin-proposal-nullish-coalescing-operator",
    "@babel/plugin-proposal-optional-chaining",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-private-methods",
    "@babel/plugin-syntax-bigint"
]

Optional Chaining

The optional chain operator ? Allows us to query objects with multiple levels without the need for various redundant pre-checks. In the past, to read the attribute value of an object, it was necessary to write some defensive pre-check codes, such as:

let second = obj && obj.first && obj.first.second;

Before accessing obj.first.second , make sure that the values ​​of obj and obj.first are not null (and not undefined). With the optional chain operator, you can greatly simplify similar cumbersome pre-check operations, and it is safer:

let second = obj?.first?.second;

If obj or obj.first is null/undefined, the expression will directly return undefined.

Nullish coalescing Operator (null value processing)

Previously for the following situations:

let v = a || "some value";

let z = a ? a : "some value";

If the value of a is 0, empty string'', false, etc., it may be meaningful, but it is considered to be a false value in the above expression, so v and z will also be assigned some value. That is:

let a = 0; // 0, ``, false may be meaningful
let v = a || "some value";
console.log(v); //>> some value

To solve this problem, ?? was born. If the value of the expression on the left side of ?? is undefined or null, the default value on the right side is returned.

let a = 0; 
let v = a ?? "some value";
console.log(v); //>> 0

let b = null; 
let z = b ?? "some value";
console.log(z); //>> some value

String.prototype.matchAll The matchAll() method returns an iterator containing all matching regular expressions and group capture results. Before matchAll appears, get all match information by calling Regexp.exec in the loop (Regexp needs to use the /g flag):

const regexp = RegExp('foo*','g');
const str ='coffe football, foosball';
while ((matches = regexp.exec(str)) !== null) {
console.log(`Found ${matches[0]}, the next round of loop starts from position ${regexp.lastIndex}`);
//>> find foo, the next cycle starts from position 9
//>> find foo, the next cycle starts from position 19
}

If you use matchAll, you do not need to use the while loop plus exec (and the regular expression needs to use the /g flag). Use matchAll to get the return value of an iterator, with for...of, array spread, Array.from() can be more convenient to implement the function.

const regexp = RegExp('foo*','g');
const str ='coffe football, foosball';
let matches = str.matchAll(regexp);
for (const match of matches) {
console.log(match);
}
//>> ["foo"]
//>> ["foo"]
//Notice:
//The iterator of matches has been consumed after for..of,
//Need to call matchAll again to create a new iterator
matches = str.matchAll(regexp);
let arr = Array.from(matches, m => m[0]);
console.log(arr);
//>> ["foo", "foo"]

import() function

This feature adds a function-like import() function to JavaScript, so that parameters can be passed in like function parameters to implement dynamic (yes, import is statically referenced) to reference modules. Below is a simple example of a single page application that demonstrates the use of import() to enable lazy loading.

<!DOCTYPE html>
<nav>
<a href="books.html" data-entry-module="books">Books</a>
<a href="movies.html" data-entry-module="movies">Movies</a>
<a href="video-games.html" data-entry-module="video-games">TV games</a>
</nav>
<main>The content will be loaded here! </main>
<script>
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`)//Dynamic reference
.then(module => {//After the module is successfully loaded, the module will be used as the parameter of the then method
module.loadPageInto(main);
})
.catch(err => {//Catch exception
main.textContent = err.message;
});
});
}
</script>

Please note the difference between import() and import: import() can be used in the script area, not only in the module; If import() is used in a module, it can be executed anywhere and at any level, instead of being promoted to the top level (priority execution); import() is executed at runtime, that is, when it runs to this sentence, the module specified by the parameter will be loaded; the parameter can also be dynamically variable, not just a static parameter; import() does not establish dependencies that can be statically analyzed (a lot of optimizations can be done in the case of static analysis), but in some simpler cases, such as import ("/foo.js"), the implementation can still perform static Analysis and optimization. If the module exposes the interface in the form of default, it can be directly obtained with the default attribute.

import('./module.js')
.then(module => {
console.log(module.default); //Get the interface exposed by the module directly through the default attribute
});

Promise.allSettled

Why is there Promise.allSettled()? For example, for example, each user fills in 3 separate forms on the page at the same time. These three forms are submitted to the backend in three interfaces. The three interfaces are independent and there is no order dependency. At this time, we need to wait until the request is completed. Prompt with the user to submit the form. When multiple promises are in progress at the same time, we will soon think of using Promise.all for packaging, but due to the one-vote veto feature of Promise.all, if any one of the three submissions fails, the subsequent forms will not Submitted, which does not meet our needs. Promise.allSettled is similar to Promise.all. Its parameter accepts an array of Promises and returns a new Promise. The only difference is that it does not have the feature of veto, which means that when all Promises are processed, we can get every The state of a Promise, regardless of whether it was successfully processed.

Promise.allSettled([Promise.resolve("coffe"), Promise.reject("1989")]).then(
  arr => {
    console.log(arr); //>> [ { status: "fulfilled", value: "coffe"},
                      //>>   { status: "rejected", reason: "1891" } ]
  }
);

globalThis globalThis is a brand new standard method to get the global this. Previously, developers would obtain it through the following methods:

  • Global variable window: is a classic method of obtaining global objects. But it cannot be used in Node.js and Web Workers

  • The global variable self: usually only takes effect in Web Workers and browsers. But it does not support Node.js. Some people will determine whether the self-identification code is running in Web Workers and browsers

  • Global variable global: only takes effect in Node.js In the past, the global object can be obtained through a global function:

// Solution before ES10
const getGlobal = function(){
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')
}
// ES10 built-in
globalThis.Array(0,1,2) // [0,1,2]
// Define a global object v = {value:true}, ES10 is defined in the following way
globalThis.v = {value:true}

The purpose of globalThis is to provide a standardized way to access global objects. With globalThis, you can get global objects in any context and at any time. If you are on a browser, globalThis will be window, and if you are on Node, globalThis will be global. Therefore, it is no longer necessary to consider different environmental issues.

// worker
globalThis === self
// node
globalThis === global
// browser
globalThis === window