The ES11 specification, completed earlier this year, introduced a number of new standards, and this article highlights some of the useful and interesting new standards

Feature preemption:

  • Private variables
  • Promise.allSettled
  • BigInt new data type
  • Nullish Coalescing Operator Specifies the Coalescing Operator
  • Optional Chaining Operator Optional chain Operator
  • Dynamic Import Dynamic Import
  • String. The prototype. New matchAll matchAll
  • GlobalThis adds a global object
  • Module Namespace Exports imports specific namespaces

Private variables

Strictly restrict some Class variables for internal use by prefixing them with # to make them private and not directly accessible outside the Class

Let’s start with a simple one

class Hero {
    #aggressivity = 0
    constructor (aggressivity){
      this.#aggressivity = aggressivity
    }
 getHurt(){  return this.#aggressivity  }  setAggressivity(aggressivity){  this.#aggressivity = aggressivity  } }  const shooter = new Hero(100) let hurt = shooter.getHurt() console.log(hurt) / / 100 console.log(shooter.#aggressivity) //Error : Uncaught SyntaxError: Private field '#aggressivity' must be declared in an enclosing class Copy the code

The # Aggressivity code cannot be accessed directly, and can only be accessed through the class, which can be modified in the same way as the common method for the same class

Let’s say the shooter is currently carryingviolentSkills, improved10% damage, we can modify the attack aggressiveness by setSAggressivity!

let aggressivity = parseInt(hurt * 1.1)
shooter.setAggressivity(aggressivity)
console.log(shooter.getHurt()) / / 110
Copy the code

Promise.allSettled

Before talking about this new feature, let’s briefly review promise. all and promise. race and speculate on why promise. allSettled is needed

Promise.all: You can wrap multiple Promise instances into a new Promise instance. Also, success and failure return different values, with success returning an array of results and failure returning the first rejected state

let p1 = new Promise((resolve, reject) = > {
  resolve('It worked')
})

let p2 = new Promise((resolve, reject) = > {
 resolve('success') })  let p3 = Promse.reject('failure')  Promise.all([p1, p2]).then((result) = > {  console.log(result) //[' success', 'success'] }).catch((error) = > {  console.log(error) })  Promise.all([p1,p3,p2]).then((result) = > {  console.log(result) }).catch((error) = > {  console.log(error) // Failed, type 'failed' }) Copy the code

Promise.race: Returns a Promise. Once a Promise triggers resolve or reject, the state of the Promise is returned

const promise1 = new Promise((resolve, reject) = > {
  setTimeout(resolve, 500.'one');
});

const promise2 = new Promise((resolve, reject) = > {
 setTimeout(resolve, 100.'two'); });  Promise.race([promise1, promise2]).then((value) = > {  console.log(value); }); // Print "two" because promise2 returns faster than promise1 Copy the code

Sometimes we may need to know all the results and do something with them, not caring if the results are successful. Before we have promise.allsettled, we need to implement promise.allSettled ourselves

let allSettled = (funcArr) = > {
  return new Promise((resolve) = > {
    let sttled = 0
    let result = []
    for(let index = 0; index<funcArr.length; index++){ const element = funcArr[index]  element  .then(res= > {  result[index] = {  status: 'fulfilled'. value: res  }  })  .catch(err= > {  result[index] = {  status: 'rejected'. reason: err  }  })  .finally((a)= > { ++sttled === funcArr.length && resolve(result) })  }  }) }  / / use const promises = [  Promise.reject('c'),  Promise.resolve('a'),  Promise.resolve('b'), ];  allSettled(promises).then(res= > {  console.log(res) })  // Print the result // [{"status":"rejected","reason":"c"}, // {"status":"fulfilled","value":"a"}, // {"status":"fulfilled","value":"b"}] Copy the code

With the new promise.AllSettled feature, we can use it without having to implement it alone

const promises = [
    Promise.reject('c'),
    Promise.resolve('a'),
    Promise.resolve('b'),
];
Promise.allSettled(promises).then(res= >{  console.log(res) }) // Print the result // [{"status":"rejected","reason":"c"}, // {"status":"fulfilled","value":"a"}, // {"status":"fulfilled","value":"b"}] Copy the code

The return result will return an array containing all the successful and failed results. Each item in the array is an object with the status attribute, which corresponds to the pity and Rejected. This is a big pity. When the state is fulfilled, it represents success, including a value, which represents the successful result. When the state is rejected, it represents failure, and there is a reason, which represents the reason for failure

BigInt

The lack of explicit integer types in JS is often confusing. Many programming languages support multiple numeric types, such as floating point, double, integer, and double, but this is not the case with JS. In JS, as defined by the IEEE 754-2008 standard, all numbers are represented in double-precision 64-bit floating-point format.

Under this standard, very large integers that cannot be accurately represented are automatically rounded. Specifically, the Number type in JS can only safely represent integers between -9007199254740991 (-(2^53-1)) and 9007199254740991(2^53-1). Any integer value outside this range may lose precision.

console.log(9999999999999999);    / / 10000000000000000
Copy the code

JS provides the number. MAX_SAFE_INTEGER constant to represent the maximum safe integer, and the number. MIN_SAFE_INTEGER constant to represent the minimum safe integer:

// Note the last digit
const A = Number.MAX_SAFE_INTEGER + 1
const B = Number.MAX_SAFE_INTEGER + 2

console.log(Number.MAX_SAFE_INTEGER) / / 9007199254740991
console.log(A) / / 9007199254740992 console.log(B) / / 9007199254740992  console.log(A === B) //true Copy the code

When the data is out of range, it will lose accuracy and not reach our expected results.

BigInt comes out of the blue and can perform arithmetic operations on large integers in standard JS without risking loss of accuracy

Creating a BigInt data type is as simple as appending an n to an integer or creating an instance with BigInt()

const bigint = 999999999999999999n
const bigintByMethod = BigInt('999999999999999999')
console.log(bigint) //999999999999999999n
console.log(bigintByMethod) //999999999999999999n
console.log(bigint === bigintByMethod) //true
 / / a Boolean value console.log(BigInt(true)) //1n console.log(BigInt(false)) //0n  console.log(typeof bigint) //"bigint"  Copy the code

BigInt and Number are two data types. They cannot be calculated and can be compared

console.log(88n= =88) //true
console.log(88n= = =88) //false
console.log(88n+1) //Error =>Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
Copy the code

All but the unary plus (+) operator can be used with BigInt

console.log(1n + 2n) //3n
console.log(1n - 2n) //-1n
console.log(+ 1n) //Uncaught TypeError: Cannot convert a BigInt value to a number
console.log(- 1n) //-1n
console.log(10n * 20n) //200n
console.log(23n%10n) //3n console.log(20n/10n) //2n .Copy the code

Note that the division operator is automatically rounded down to the nearest integer

console.log(25n / 10n) //2n
console.log(29n / 10n) //2n
Copy the code

Last but not least, Boolean and BigInt are converted to Number and BigInt is treated as true if not 0n

if (5n) {
    // Here the code block will be executed
}

if (0n) {
 // The code block is not executed } Copy the code

Nullish Coalescing Operator Specifies the Coalescing Operator

Add a logical operator?? , handling null, and undefined, working principle and the logical or (| |), but on the contrary

| | if is true that the return on the left side of the value, or return to the right

0 || 5 // return 5
"" || 5 // return 5
"Dried prawn with dried pork" || 'V5' //return ""
Copy the code

?? If null or undefined, return to the right, otherwise return to the left

0 ?? 5 //return 0
"" ?? 5 //return ""
null ?? 5 // return 5
undefined ?? 5 // return 5
false ?? 5 //return false
NaN ?? 5 // return NaN Copy the code

When using the?? Operator, one thing to be aware of

  • Cannot be used with other operator, for example, &&, | |
  • But if wrapped in parentheses, it can be used in combination
"Dried prawn with dried pork" || undefined ?? "Sneaker" //Uncaught SyntaxError: Unexpected token '?? '
"Dried prawn with dried pork" && undefined ?? "Sneaker" //Uncaught SyntaxError: Unexpected token '?? '

("Dried prawn with dried pork" || undefined)??"(๑ • ̀ ㅂ ́) organisation ✧" //
("Dried prawn with dried pork" && null)??"Learning together" //" Learn together"
Copy the code

Optional Chaining Operator Optional chain Operator

In daily development, many developers encounter Cannot read property XXX of undefined, which throws a field that Cannot be read from undefined data

When looking for nested objects, the optional chain operator terminates as soon as it finds the first undefined or null in the chain and returns undefined instead of continually looking down and causing an error

const obj = {
  foo: {
    bar: {
      baz: 42.    },
 }, } console.log(obj.fo.bar.baz) //Uncaught TypeError: Cannot read property 'bar' of undefined  In objects such as these, we usually perform data security checks to access nested objects, otherwise we will throw errorsif(obj&&obj.foo&&obj.foo.bar){  console.log(obj.foo.bar.baz) / / 42 } Copy the code

Now that the optional chain operator is available, we just need to read the property like this

console.log(obj? .foo? .bar? .baz)/ / 42
            
console.log(obj.foo.bar? .baz)/ / 42
Copy the code

Dynamic Import Dynamic Import

In standard import imports, they are static, and all imported modules are compiled at load time, not on demand. Whenever we need conditional imports, we can only use require().

But now, we have a way to improve this situation, because dynamic imports can effectively reduce the compilation of unused code, improve the first screen load speed, and load on demand. So why do we need dynamic imports?

  • Static imports consume load time, and many modules do not need to render the first screen
  • Static imports consume a lot of memory at import time
  • There may be modules that do not exist when they are loaded
  • Reduce some of the side effects of conditional dependence
// General import mode
import("/module/sneaker/test.js")
.then(module= > {
 // Module related operations
})
 //await const getModule = await import("/module/sneaker/test.js")  // async await const getUserInfo = async (hasLogin) => {  if(! hasLogin){ const user = await import("/module/sneaker/user.js")  user.getInfo()  } } Copy the code

matchAll

A new method based on the String prototype allows us to match a String and a regular expression, and the return value is an iterator of all the matches. Can be done through for… Of traversal or operator… , array. from converts the result iterator to an Array

const string = 'Hello Sneaker,Where is the library? '
const regex = /[A-W]/g
const matches = string.matchAll(regex)

console.log(... matches)//["H", index: 0, input: "Hello Sneaker,Where is the library?", groups: undefined]  //["S", index: 6, input: "Hello Sneaker,Where is the library?", groups: undefined]  //["W", index: 14, input: "Hello Sneaker,Where is the library?", groups: undefined]  Copy the code

globalThis

This is to provide a standard way to access global objects. In the browser, we can use window/self/frames to access global objects, but for Web Workers, we can only use self. In Node, we need to use global.

Before globalThis becomes standard, we may need to do this to get global objects

const globalObj = (() = >{
 if(self) return self
  if(window) return window
  if(global) return global
  throw new Error('Sorry! No global obj found')
}) Copy the code
//Browser 
globalThis === window //true

//Webworker
globalThis === self //true
 //Node globalThis === global //true Copy the code

From now on the realization of the global object of unity!

Module Namespace Exports imports specific namespaces

export * as ns from './module

/ / is equivalent toimport * as ns from '. /module'
export {ns} Copy the code

Importing a specific namespace does not actually import the module, but merely forwards the module, making it impossible to use the module directly in the module

reference

  • ecma-262
  • MDN

The last

There are lots of features, but some of them are fun, like optional chains and void merge operators, and they work every time, but you have to try them out. New features are usually not written or easy to ignore forgotten, it is recommended that we can often subconsciously review and use, learn and grow together.