This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.


ES6 — ES12 common new features, many features in the development is still very practical, I hope to help you a little bit! The article content is more, the suggestion collects in the study first!

ECMAScript is a scripting programming language standardized by Ecma International through ECMA-262. The language is called JavaScript. Simply put, ECMAScript is the standard and specification of JavaScript, and JavaScript is the implementation and extension of the ECMAScript standard.

Since 2015, ECMAScript has been published in the following versions:

Release time Official name The name of the version The name of the abbreviations
2015 ECMAScript2015 ECMAScript6 ES2015, ES6
2016 ECMAScript2016 ECMAScript7 ES2016, ES7
2017 ECMAScript2017 ECMAScript8 ES2017, ES8
2018 ECMAScript2018 ECMAScript9 ES2018, ES9
2019 ECMAScript2019 ECMAScript10 ES2019, ES10
2020 ECMAScript2020 ECMAScript11 ES2020, ES11
2021 ECMAScript2021 ECMAScript12 ES2021, ES12

Let’s take a look at some of the techniques available in each version of ECMAScript.** Note: ** part of the knowledge point has been introduced in the previous article, this article will not repeat, the paper has attached the corresponding article link.

1. New ES6 Features (2015)

The update of ES6 is mainly reflected in the following aspects:

  • Expression: variable declaration, destruct assignment
  • Built-in objects: string extension, value extension, object extension, array extension, function extension, regular extension, Symbol, Set, Map, Proxy, Reflect
  • Statements and operations: Class, Module, Iterator
  • Asynchronous programming: Promise, Generator, Async.

This section describes some common new features. There are also some features, which have been introduced in previous articles, but are not covered here.

  • Promise, Generator: “Swastika: Relearning JavaScript Asynchronous Programming”
  • Array methods: “Swastika: Relearning JavaScript Array Types”
  • String Methods: “JavaScript 28 Common String Methods and Techniques”

1. Let and const

In ES6, we added the let keyword, which is used to declare variables, and the const keyword, which is usually used to declare constants. Let and const have the following characteristics compared with var keyword:

features var let const
Variable ascension ✔ ️ x x
The global variable ✔ ️ x x
Repeat statement ✔ ️ x x
To assign a value ✔ ️ ✔ ️ x
Temporary dead zone x ✔ ️ ✔ ️
Block scope x ✔ ️ ✔ ️
Only declare not initialize ✔ ️ ✔ ️ x

Here are four of them:

(1) Reassign

Variables declared by the const keyword are “unmodifiable”. What const guarantees is not that the value of a variable cannot be changed, but that the memory address to which the variable refers cannot be changed. For data of primitive types (values, strings, booleans), the value is stored at the memory address to which the variable points and is therefore equivalent to a constant. But in the case of reference types (mainly objects and arrays), the variable refers to the memory address of the data and holds only a pointer. Const guarantees that the pointer is invariant, and that the data structure it points to is out of control.

(2) block-level scope

Before we introduced let and const, there was no block-level scope. This caused many problems, such as inner variables overwriting outer variables of the same name:

var a = 1;
if (true) {
  var a = 2;
}

console.log(a);   // Output: 2
Copy the code

Loop variables are leaked as global variables:

var arr = [1.2.3];
for (var i = 0; i < arr.length; i++) {
  console.log(arr[i]);  // Output: 1, 2, 3
}

console.log(i); // Output: 3
Copy the code

Block-level scoped variables defined by lets and const do not cause this problem:

let a = 1;
if (true) {
  let a = 2;
}

console.log(a); // Output: 1

const arr = [1.2.3];
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);  // Output: 1, 2, 3
}

console.log(i); // Uncaught ReferenceError: i is not defined
Copy the code

(3) Variable promotion

We know that variable promotion existed before ES6, and by variable promotion a variable can be used before it is declared:

console.log(a); // Output result: undefined
var a = 1;
Copy the code

The essence of variable promotion is that the JavaScript engine compiles and analyzes the code before executing it, adding the detected variables and function declarations to memory called Lexical Environment in the JavaScript engine. And give an initialization value undefined. And then we go into the code execution phase. So the declared variables and functions are known to the JS engine before the code is executed.

This is not intuitive, so in ES6, the let and const keywords limit variable promotion, and variables defined by let are added to the Lexical Environment and not initialized to undefined. The JS engine will only initialize when it comes to lexical declarations and assignments. While variables are not accessible or usable during the time span between their creation and actual initialization, ES6 calls this a temporary dead zone:

// Temporary dead zone begins
a = "hello";     // Uncaught ReferenceError: Cannot access 'a' before initialization

let a;   
// The temporary dead zone is over
console.log(a);  // undefined
Copy the code

(4) Repeat the statement

Before ES6, there was no limit to the number of variables declared by the var keyword that could be repeated within a scope, or even declared with the same name as a parameter. Neither of the following functions can fail:

function funcA() {
  var a = 1;
  var a = 2;
}

function funcB(args) {
  var args = 1; 
}
Copy the code

Let fixes this loose design:

function funcA() {
  let a = 1;
  let a = 2;  // Uncaught SyntaxError: Identifier 'a' has already been declared
}

function funcB(args) {
  let args = 1;  // Uncaught SyntaxError: Identifier 'args' has already been declared
}
Copy the code

We have now abandoned var entirely in our project, using let to define variables and const to define constants. The following rules are enabled in ESlint:

"no-var": 0;
Copy the code

2. Deconstruct the assignment

ES6 also introduced the concept of deconstructed assignment, which follows “pattern matching,” whereby the variable on the left is assigned a corresponding value as long as the patterns on both sides of the equal sign are equal. Different types of data are deconstructed in different ways. The following is a look at the deconstruction methods of different types of data.

In normal development, I mainly use deconstructed assignment of objects, such as deconstructing porps value in React to obtain the value from the parent component. UseState in React Hooks uses destruct assignment of arrays;

(1) Array deconstruction

Any data structure with an Iterator interface can be assigned in the form of an array.

const [foo, [[bar], baz]] = [1The [[2].3]].console.log(foo, bar, baz) // Output: 1, 2, 3
Copy the code

Here, ES6 implements the structure of an array and assigns the variables foo, bar, and baz in turn. Destructuring an array assigns values to variables by position.

Arrays can also be partially deconstructed, with only parts of their contents:

const [x, y] = [1.2.3];   // Extract the first two values
const [, y, z] = [1.2.3]  // Extract the last two values
const [x, , z] = [1.2.3]  // Extract the first three values
Copy the code

The variable is assigned undefined if there is no value at the corresponding position:

const [x, y, z] = [1.2]; 
consoleThe log (z)// Output result: undefined
Copy the code

Array destructuring assignments can use the REST operator to capture remaining items:

const [x, ...y] = [1.2.3];   
console.log(x);  // Output: 1
console.log(y);  // Output result: [2, 3]
Copy the code

Default values are also supported for deconstruction, and are used only when the corresponding value is undefined:

const [x, y, z = 3] = [1.2]; 
consoleThe log (z)// Output: 3
Copy the code

(2) Object deconstruction

The nature of object deconstruction assignment is to first find the property of the same name, and then assign to the corresponding variable:

let { foo, bar } = { foo: 'aaa'.bar: 'bbb' };
console.log(foo, bar); // Output result: aaa BBB
Copy the code

It is important to note that in JavaScript, the attributes of an object are not sequential. Therefore, when deconstructing assignments, variables must have the same name as attributes to fetch values.

Destruct assignment of an object also supports default values, which take effect only when the defined variable does not exist in the object:

let { foo, bar, baz = 'ccc'} = { foo: 'aaa'.bar: 'bbb'.baz: null };
console.log(foo, bar, baz); Aaa BBB null

let { foo, bar, baz = 'ccc'} = { foo: 'aaa'.bar: 'bbb' };
console.log(foo, bar, baz); Aaa BBB CCC
Copy the code

As you can see, the default value is valid only if the defined variable is strictly ===undefined.

In addition, we also need to pay attention to, can’t give variables declared in the assignment, because when the lack of the let, const, var keywords, will understand the {baz} for code block which can lead to syntax errors, so the following code will be an error:

let baz;
{ baz } = { foo: 'aaa'.bar: 'bbb'.baz: 'ccc' };
Copy the code

We can solve this problem by wrapping the entire destruct assignment statement in parentheses:

let baz;
({ baz } = { foo: 'aaa'.bar: 'bbb'.baz: 'ccc' });
console.log(baz)
Copy the code

In destructuring assignment of an object, we can assign the methods of an existing object to a variable, such as:

let { log, sin, cos } = Math;
log(12)  // The output is 2.4849066497880004
sin(1)   // The output is 0.8414709848078965
cos(1)   // Output result: 0.5403023058681398
Copy the code

(3) Other deconstruction assignments

The remaining deconstruction assignments, which I have used less frequently in projects so far, are briefly reviewed.

  • String deconstruction

If the value to the right of the equals sign is not an object or array, convert it to an array-like object.

const [a, b, c, d, e] = 'hello';
console.log(a, b, c, d, e)  // Output result: h e L L O
Copy the code

Array-like objects have a length property, so we can destruct this property and assign it:

let {length} = 'hello';    // Output: 5
Copy the code

Since a string is a constant, we usually know what its value is, so destructively assigning variables is rarely used.

  • Numeric and Boolean deconstruction assignments

When numeric and Boolean values are deconstructed, they are converted to objects before the destruct syntax is applied:

let {toString: s} = 123;
s === Number.prototype.toString // Output result: true

let {toString: s} = true;
s === Boolean.prototype.toString // Output result: true

Copy the code

Note that null and undefined cannot be converted to objects, so an error will be reported if these two values are on the right-hand side.

  • Function argument destruct assignment

Function arguments are ostensibly arrays that are deconstructed into x and y the moment they are passed in.

function add([x, y]){
  return x + y;
}
add([1.2]);   / / 3
Copy the code

In addition, we can also deconstruct the return value of the function:

function example() {
  return [1.2.3];
}
let [a, b, c] = example();
Copy the code

3. Template string

In traditional JavaScript language, output templates often use the form of string concatenation, which is quite cumbersome to write. In ES6, the concept of template string is introduced to solve the above problems.

Template strings are enhanced strings, identified by backquotes, that can be used to define single-line strings, multi-line strings, or to embed variables in strings.

// Embed variables in strings
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}? `

// Call a function in a string
` ${fn()} 
Copy the code

In normal development, in addition to the application in the above code, template strings will be used in many places, such as concatenating a DOM string, defining the DOM structure in the Emotion/ Styled mode, etc. However, there is no code prompt for defining DOM elements in template strings.

When using template strings, note the following:

  • If you use backquotes in a string, use \ to escape;
  • If there are Spaces and indentations in a multi-line string, they are kept in the output;
  • ${}; ${};
  • Template strings can put arbitrary expressions, perform operations, reference object properties, and even call functions;
  • An error is reported if variables in template characters are not declared.

4. Default function parameters

Before ES6, functions did not support default arguments, ES6 implemented this support and only fired defaults if no arguments were passed:

function getPoint(x = 0, y = 0) {
  console.log(x, y);
}

getPoint(1.2);   / / 1. 2
getPoint()        // 0  0 
getPoint(1)       // 1  0
Copy the code

When using function defaults, note the following:

(1) Function length attribute value

The length attribute is usually used to represent the number of arguments to the function. When the default value is introduced, length represents the number of normal arguments before the first parameter with a default value:

const funcA = function(x, y) {};
console.log(funcA.length);  // Output: 2

const funcB = function(x, y = 1) {};
console.log(funcB.length);  // Output: 1

const funcC = function(x = 1, y) {};
console.log(funcC.length);  // Output 0
Copy the code

(2) Parameter scope

When a function parameter is initialized with a default value, the parameter will form a separate scope. After initialization, the scope will be dissolved:

let x = 1;

function func(x, y = x) {
  console.log(y);
}

func(2);  
Copy the code

This will eventually print a 2. When the function is called, the arguments x, y form a separate scope, so the y in the argument is equal to the x in the first argument, rather than the 1 defined above.

5. Arrow function

Arrow functions were introduced in ES6 to simplify the definition of functions:

const counter = (x, y) = > x + y;
Copy the code

Compared with ordinary functions, arrow functions have the following characteristics:

(1) More concise

  • If there are no arguments, just write an empty parenthesis
  • If you have only one argument, you can omit the parentheses for the argument
  • If there are multiple arguments, separate them with commas
  • If the function body returns only one sentence, you can omit the braces
// 1. No arguments are passed
const funcA = () = > console.log('funcA');
/ / equivalent to the
const funcA = function() {
  console.log('funcA');
} 

// 2. Pass in parameters
const funcB = (x, y) = > x + y;
/ / equivalent to the
const funcB = function(x, y) {
  return x + y;
} 

// 3. Simplification of a single parameter
const funcC = (x) = > x;
// For a single parameter, drop () and simplify to
const funcC = x= > x;
/ / equivalent to the
const funcC = function(x) {
  return x;
}

// 4. The above code has only one statement. If there are multiple statements, use {}.
const funcD = (x, y) = > { console.log(x, y); return x + y; }
/ / equivalent to the
const funcD = function(x, y) {
  console.log(x, y);
  return x + y;
}
Copy the code

(2) Do not bind this

The arrow function doesn’t create its own this, so it doesn’t have its own this, it just inherits this at the level above its scope. So the orientation of this in the arrow function is already defined when it is defined, and will not change after that.

var id = 'GLOBAL';
var obj = {
  id: 'OBJ'.a: function(){
    console.log(this.id);
  },
  b: () = > {
    console.log(this.id); }}; obj.a();// 'OBJ'
obj.b();    // 'GLOBAL'
new obj.a()  // undefined
new obj.b()  // Uncaught TypeError: obj.b is not a constructor
Copy the code

The object obj’s method B is defined using the arrow function. This in this function always refers to this in the global execution environment in which it was defined. Even though this function is called as a method on obj, this still refers to the Window object. Note that the curly braces {} that define the object do not form a single execution environment; it is still in the global execution environment.

Similarly, using call(), apply(), bind(), etc., does not change the direction of this in the arrow function:

var id = 'Global';
let fun1 = () = > {
    console.log(this.id)
};
fun1();                     // 'Global'
fun1.call({id: 'Obj'});     // 'Global'
fun1.apply({id: 'Obj'});    // 'Global'
fun1.bind({id: 'Obj'}) ();// 'Global'
Copy the code

(3) Not available as a constructor

The constructor new operator performs the following steps:

  1. Create an object
  2. Assign the constructor’s scope to the new object (that is, point the object’s __proto__ attribute to the constructor’s Prototype attribute)
  3. Refers to the code in the constructor, where this refers to the object (that is, to add properties and methods to the object)
  4. Return a new object

In fact, the second step is to point this in the function to that object. However, arrow functions cannot be used as constructors because they do not have their own this, and this refers to the outer execution environment and cannot be changed.

(4) Don’t bind arguments

Arrow functions have no arguments objects of their own. Calling Arguments in the arrow function actually gets the arguments value for its outer function.

6. Extend operators

Extension operators:… Like the reverse of the rest argument, unpack an array into a comma-separated sequence of arguments.

The spread extension operator has the following uses:

(1) Convert an array to a comma-separated sequence of arguments:

function  test(a,b,c){
    console.log(a); / / 1
    console.log(b); / / 2
    console.log(c); / / 3
}

var arr = [1.2.3]; test(... arr);Copy the code

(2) Join a group of numbers to another array:

var arr1 = [1.2.3.4];
var arr2 = [...arr1, 4.5.6];
console.log(arr2);  // [1, 2, 3, 4, 4, 5, 6]

Copy the code

(3) Convert strings to comma-separated arrays:

var str='JavaScript';
var arr= [...str];
console.log(arr); // ["J", "a", "v", "a", "S", "c", "r", "i", "p", "t"]
Copy the code

7. Symbol

A new base data type, Symbol, was introduced in ES6 to represent unique values. It is a string-like data type with the following characteristics:

  • The value of Symbol is unique and is used to resolve naming conflicts
  • The Symbol value cannot be computed with other types of data
  • Object properties defined by Symbol cannot be usedfor... inTraversal loops, but can be usedReflect.ownKeysTo get all the key names of the object
let s1 = Symbol(a);console.log(typeof s1); // "symbol"

let s2 = Symbol('hello');
let s3 = Symbol('hello');
console.log(s2 === s3); // false
Copy the code

Based on the above properties, the Symbol attribute type works well in two types of scenarios: constant values and object attributes.

(1) Avoid constant value repetition

The getValue function performs the corresponding code logic based on the string argument key passed in:

function getValue(key) {
  switch(key){
    case 'A':...case 'B':... } } getValue('B');
Copy the code

This code is very unfriendly to callers because it uses a Magic string (a specific string or number that appears multiple times in the code and is strongly coupled to the code), When the getValue function is called, you need to look at the function code to find the optional value of the key parameter. So we can declare the value of the key argument as a constant:

const KEY = {
  alibaba: 'A'.baidu: 'B',}function getValue(key) {
  switch(key){
    case KEY.alibaba:
      ...
    case KEY.baidu:
      ...
  }
}
getValue(KEY.baidu);
Copy the code

However, this is not perfect. If we were to add a KEY to the KEY constant, it would be very likely that the value would duplicate according to the corresponding rules:

const KEY = {
  alibaba: 'A'.baidu: 'B'.tencent: 'B'
}
Copy the code

This is where the problem arises:

getValue(KEY.baidu) // same as getValue(key.tencent)
Copy the code

Therefore, it is more appropriate to use Symbol in this scenario. We do not need to care about the value itself, only the uniqueness of the value:

const KEY = {
  alibaba: Symbol(),
  baidu: Symbol(),
  tencent: Symbol()}Copy the code

(2) Avoid object attribute overwriting

The fn function needs to add a temporary attribute user to the object argument passed in, but it may already have this attribute in the object argument, and if assigned directly it overwrites the previous value. Use symbols to avoid this problem. Create a variable of the Symbol data type, and then assign and read that variable as an attribute of the object parameter to avoid overwriting:

function fn(o) { // {user: {id: xx, name: yy}}
  const s = Symbol()
  o[s] = 'zzz'
}
Copy the code

8. Set the Set

ES6 provides a new data structure called a Set. It is similar to an array, but the values of the members are unique. Collections implement the Iterator interface, so you can use extension operators and for… Of traverses.

Set properties and methods:

Properties and methods An overview of the
size Returns the number of elements in the collection
add Adds a new element that returns the current collection
delete Deletes the element, returning a Boolean value
has Checks if the collection contains an element, returning a Boolean
clear Empty the collection, return undefined
// Create an empty collection
let s = new Set(a);// Create a non-empty collection
let s1 = new Set([1.2.3.1.2.3]);
// Returns the number of elements in the collection
console.log(s1.size);       / / 3
// Add a new element
console.log(s1.add(4));     / / {1, 2, 3, 4}
// Delete elements
console.log(s1.delete(1));  //true
// Check if a value exists
console.log(s1.has(2));     // true
// Clear the collection
console.log(s1.clear());    //undefined
Copy the code

Due to the uniqueness of the elements in the set, we can use set to implement array de-duplication in practical applications:

let arr = [1.2.3.2.1]
Array.from(new Set(arr))  / / {1, 2, 3}
Copy the code

The array.form () method is used to convert an Array collection to an Array.

We can use set to find the intersection and union of two arrays:

// create an intersection
let intersection = new Set([...set1].filter(x= > set2.has(x)));

// set the difference
let difference = new Set([...set1].filter(x= >! set2.has(x)));Copy the code

Arrays and collections can be converted to each other using the following methods:

// Set sets are converted to arrays
const arr = [...mySet]
const arr = Array.from(mySet)

// Array is converted to Set
const mySet = new Set(arr)
Copy the code

9. Map

ES6 provides a Map data structure, which is similar to an Object and also a collection of key-value teams. However, its key-value range is not limited to strings, but can be any type of value (including Object), that is, the Object structure provides “string-value” correspondence, while the Map structure provides “value-value” correspondence. Is a more sophisticated implementation of Hash structures. If you need a key-value data structure, Map is better than Object. Map also implements the Iterator interface, so you can use extension operators and for… Of traverses.

Map attributes and methods:

Properties and methods An overview of the
size Returns the number of elements in the Map
set Add a new element that returns the current Map
get Returns the key value of the keyname object
has Checks if the Map contains an element, returning a Boolean value
clear Clear Map and return undefined
// Create an empty map
let m = new Map(a);// Create a non-empty map
let m2 = new Map([['name'.'hello']]);// Get the number of mapping elements
console.log(m2.size);          / / 1
// Add the mapping value
console.log(m2.set('age'.6)); // {"name" => "hello", "age" => 6}
// Get the mapping value
console.log(m2.get('age'));    / / 6
// Check if the mapping exists
console.log(m2.has('age'));    // true
/ / remove
console.log(m2.clear());       // undefined
Copy the code

Note that the Map structure only treats references to the same object as the same key:

let map = new Map(a); map.set(['a'].555); 
map.get(['a']) // undefined
Copy the code

The set and GET methods in the code above, ostensibly for the same key, are actually two values, the memory address is different, so the get method cannot read the key, so it returns undefined.

Map keys are actually bound to memory addresses. As long as the memory addresses are different, they are regarded as two keys. This solves the clash problem, and when extending the library, you don’t have to worry about your property having the same name as the original property if you use an object as a key name.

If the Map’s key is a value of a simple type (number, string, Boolean), as long as the two values are strictly equal, the Map treats it as a key, including 0 and -0. Also, although NaN is not strictly equal to itself, Map treats it as the same key.

let map = new Map(a); map.set(NaN.123); 
map.get(NaN) / / 123
map.set(-0.123); 
map.get(+0) / / 123
Copy the code

10. Modular

ES6 introduced the first modular development specification ES Module, enabling Javascript to support native modular development for the first time. ES Module treats a file as a Module, and each Module has its own independent scope. How do you link each Module together? The core point is the import and export of modules.

(1) Export export module

  • Normal export:
/ / way
export var first = 'test';
export function func() {
    return true;
}

2 / / way
var first = 'test';
var second = 'test';
function func() {
    return true;
}
export {first, second, func};
Copy the code
  • As keywords:
var first = 'test';
export {first as second};
Copy the code

The AS keyword can rename exposed variables or methods, and the same variable can be exposed multiple times after being renamed.

  • export default

Export default exports the default output, that is, you do not need to know the name of the output in the module. You can specify any name for the output when importing it.

/ / export
export default function () {
  console.log('foo');
}
/ / import
import customName from './export-default';
Copy the code

Note: Curly braces are not required when importing the default module. Exported variables or methods can have names, but are invalid externally. Export default can be used only once.

(2) import import module

  • Normal import:
import {firstName, lastName, year} from './profile'; Copy the codeCopy the code

Import module location can be relative path or absolute path,.js can be omitted, if there is no path but the module name, you need to tell the engine through the configuration file to find the location.

  • As keywords:
import { lastName as surname } from './profile';
Copy the code

The import command is promoted to the head of the module, so the location of the write is not as important, but expressions and variables cannot be used to import.

  • Load the entire module (no output)
import 'lodash'; // It is only loaded and cannot be used
Copy the code
  • Load the entire module (with output)
import * as circle from './circle';
console.log('Circle area:' + circle.area(4));
console.log('Circumference:' + circle.circumference(14));
Copy the code

Note: import * ignores the default output

(3) import/export compound usage

  • First in, then out
export { foo, bar } from 'my_module';
/ / is equivalent to
import { foo, bar } from 'my_module';
export { foo, boo};
Copy the code
  • Overall import and output and default
// Total output
export * from 'my_module';
// Export default. As mentioned earlier, export default actually exports the default variable
export { default } from 'foo';
// Change the named interface to default
export { es6 as default } from './someModule';
Copy the code

(4) Module inheritance

export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
  return Math.exp(x);
}
Copy the code

Note: export * ignores default.

Ii. New ES7 Features (2016)

1. Array.prototype.includes

The includes() method is used to determine whether an array contains a specified value and returns true if it does, false otherwise. This method does not change the original array. The syntax is as follows:

arr.includes(searchElement, fromIndex)
Copy the code

This method takes two parameters:

  • SearchElement: Mandatory, element value to be found.
  • FromIndex: Optional, starting from the fromIndex index to find the target value. If it is negative, the search starts in ascending order from array.length + fromIndex’s index (even if you skip the fromIndex absolute index from the end and then search backwards). The default is 0.
[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
Copy the code

Prior to ES7, indexOf was commonly used to determine whether an array contained a specified value. However, indexOf is semantically unambiguous and intuitive, and internal indexOf uses === to judge NaN. Includes fixes this problem:

[1.2.NaN].indexOf(NaN);   // -1
[1.2.NaN].includes(NaN);  // true
Copy the code

Note: Comparing strings and characters using includes() is case sensitive.

2. Index operators

ES7 also introduces the exponential operator, which is equivalent to math.pow (), to make it easier to evaluate exponents:

Math.pow(2.10));  / / 1024
2**10;           / / 1024
Copy the code

3. New ES8 features (2017)

ES8 has introduced async/await solutions for asynchronous functions. I won’t go into more details here.

1. PadStart () and padEnd ()

The padStart() and padEnd() methods complement the length of the string. If a string is not of a specified length, the header or tail is completed.

(1) padStart ()

PadStart () is used for head completion. The method takes two arguments, the first of which is a number representing the length of the string after completion. The second argument is the string used for completion.

If the length of the original string is equal to or greater than the specified minimum length, the original string is returned:

'x'.padStart(1.'ab') // 'x'
Copy the code

If the sum of the length of the completion string and the original string exceeds the specified minimum length, the completion string exceeding the number of bits is truncated:

'x'.padStart(5.'ab') // 'ababx'
'x'.padStart(4.'ab') // 'abax'
Copy the code

If the second argument is omitted, the default space is used to complete the length:

'x'.padStart(4.'ab') // 'a '
Copy the code

A common use of padStart() is to specify the number of digits for numeric completion. A recent requirement of the author was to complete the number of pages returned by three digits, such as 001 on page 1, using this method:

"1".padStart(3.'0')   // Output result: '001'
"15".padStart(3.'0')  // Output result: '015'
Copy the code

(2) padEnd ()

PadEnd () is used for tail completion. This method also takes two parameters, the first parameter is the maximum length that string completion takes effect, and the second parameter is the string used for completion:

'x'.padEnd(5.'ab') // 'xabab'
'x'.padEnd(4.'ab') // 'xaba'
Copy the code

(2) the Object) values (“) and (Object) entries ()

Keys methods were introduced in ES5, and object. values and object. entries were introduced in ES8 as complementary means of iterating through an Object for… Of recycling. They are used to iterate over an object, which returns an array of the given object’s own enumerable attributes (excluding the inherited and Symbol attributes) in the same order as the normal loop over the object. The three elements return the following values:

  • Object.keys() : Returns an array of Object keys;
  • Object.values() : Returns an array of Object keys and values;
  • Object.entries() : Returns an array of Object key names and key values.
let obj = { 
  id: 1.name: 'hello'.age: 18 
};
console.log(Object.keys(obj));   ['id', 'name', 'age']
console.log(Object.values(obj)); [1, 'hello', 18]
console.log(Object.entries(obj));   / / output: [[' id ', 1], [' name ', 'hello,'], [18] 'age,
Copy the code

Pay attention to

  • The array.keys () method returns all the values in the array as strings, meaning that keys that are not strings are converted to strings.
  • Attribute values in the resulting array are enumerable attributes of the object itself, excluding inherited attributes.

3. Function extension

ES2017 specifies that a function’s argument list can end with a comma:

function person( name, age, sex, ) {}
Copy the code

The main purpose of this feature is to make it easier to modify the same function and reduce unnecessary line changes when using Git for collaborative development.

4. New ES9 features (2018)

1. The for await… of

for await… The of method is called an asynchronous iterator and is used primarily to iterate over asynchronous objects.

for await… The of statement creates an iteration loop over asynchronous or synchronous iterables, including Strings, arrays, array-like arrays, maps, sets, and custom asynchronous or synchronous iterables. This statement can only be used within async function:

function Gen (time) {
  return new Promise((resolve,reject) = > {
    setTimeout(function () {
       resolve(time)
    },time)
  })
}

async function test () {
   let arr = [Gen(2000),Gen(100),Gen(3000)]
   for await (let item of arr) {
      console.log(Date.now(),item)
   }
}
test()
Copy the code

Output result:

2. Promise.prototype.finally()

ES2018 adds the finally() method to promises, which represents a method that will be executed regardless of whether a Promise instance ultimately succeeds or fails:

const promise = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    const one = '1';
    reject(one);
  }, 1000);
});

promise
  .then(() = > console.log('success'))
  .catch(() = > console.log('fail'))
  .finally(() = > console.log('finally'))
Copy the code

The finally() function takes no arguments, and the execution result of a Promise instance is usually not known inside finally(), so operations unrelated to the promise state are usually performed inside finally() methods.

3. Extension operators for objects

In ES6, the extension operator was introduced, but it only works on arrays. In ES2018, the extension operator works on objects:

(1) Organize elements into objects

const obj = {a: 1.b: 2.c: 3};
const{a, ... rest} = obj;console.log(rest);    {b: 2, c: 3}

(function({a, ... obj}) {
  console.log(obj);    {b: 2, c: 3}} ({a: 1.b: 2.c: 3}));
Copy the code

(2) Extend objects to elements

const obj = {a: 1.b: 2.c: 3};
constnewObj ={... obj,d: 4};
console.log(newObj);  {a: 1, b: 2, C: 3, d: 4}
Copy the code

(3) Can be used to merge objects

const obj1 = {a: 1.b:2};
const obj2 = {c: 3.d:4};
constmergedObj = {... obj1, ... obj2};console.log(mergedObj);  {a: 1, b: 2, C: 3, d: 4}
Copy the code

5. New ES10 Features (2019)

1. TrimStart () and trimEnd ()

Prior to ES10, JavaScript provided the trim() method to remove whitespace at the beginning and end of a string. In ES9, trimStart() and trimEnd() methods are proposed to remove whitespace at the beginning and end of a string, including whitespace, TAB, line feed and other whitespace.

(1) trimStart ()

The trimStart() method behaves like trim(), but returns a new string with whitespace removed from the beginning of the original string, without modifying the original string:

const s = ' abc ';

s.trimStart()   // "abc "
Copy the code

(2) trimStart ()

The trimEnd() method behaves like trim(), but returns a new string with whitespace removed from the end of the original string, without modifying the original string:

const s = ' abc ';

s.trimEnd()   // " abc"
Copy the code

Note that neither method works with the null, undefined, or Number types.

2. The flat () and flatMap ()

(1) flat ()

In ES2019, the flat() method is used to create and return a new array containing the same elements as the array it called Flat (), except that any elements that are themselves arrays are flattened and filled into the returned array:

[1[2.3]].flat()        / / [1, 2, 3]
[1[2[3.4]]].flat()   // [1, 2, [3, 4]]
Copy the code

Flat () only flattens one level by default when no arguments are passed. If you want to flatten more levels, you need to pass flat() a numeric parameter that indicates the number of levels to flatten:

[1[2[3.4]]].flat(2)  // [1, 2, 3, 4]
Copy the code

If there is an empty item in the array, it is skipped:

[1[2.3]].flat());    //  [1, 2, 3]
Copy the code

If the argument passed is less than or equal to 0, the original array is returned:

[1[2[3[4.5]]]].flat(0);    // [1, [2, [3, [4, 5]]]]
[1[2[3[4.5]]]].flat(-10);  // [1, [2, [3, [4, 5]]]]
Copy the code

(2) flatMap ()

The flatMap() method uses a mapping function to map each element and then compresses the result into a new array. It is almost identical to a map and a flat connected to a depth value of 1, but flatmaps are usually slightly more efficient when combined into one method. This method returns a new array where each element is the result of the callback function and the structure depth is 1.

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

[1.2.3.4].flatMap(x= > [[x * 2]]);  [[2], [4], [6], [8]]
[1.2.3.4].map(x= > [x * 2]);        [[2], [4], [6], [8]]
Copy the code

3. Object.fromEntries()

The object.fromentries () method converts the list of key-value pairs into an Object. This method is the reverse of the object.entries () method. The Object.entries() method returns an array of key-value pairs for a given Object’s own enumerable properties, while the Object.fromentries () method converts the list of key-value pairs to an Object.

const object = { key1: 'value1'.key2: 'value2' }
 
const array = Object.entries(object)  // [ ["key1", "value1"], ["key2", "value2"] ]
 
 
Object.fromEntries(array)             // { key1: 'value1', key2: 'value2' }

Copy the code

This method is used for the following two purposes:

(1) Convert an array to an object

const entries = [
  ['foo'.'bar'],
  ['baz'.42]]Object.fromEntries(entries)  // { foo: "bar", baz: 42 }
Copy the code

(2) Convert the Map into an object

const entries = new Map([['foo'.'bar'],
  ['baz'.42]])Object.fromEntries(entries)  // { foo: "bar", baz: 42 }
Copy the code

4. Description Symbol

When creating a Symbol through Symbol(), we can provide a string as a description with an argument:

let dog = Symbol("dog");  // dog is a description
Copy the code

Prior to ES2019, obtaining a description of a Symbol value required either the String method or the toString method:

String(dog);              // "Symbol(dog)" 
dog.toString();           // "Symbol(dog)" 
Copy the code

ES2019 adds the attribute Description to access the description directly:

dog.description;  // dog
Copy the code

5. toString()

ES2019 extends the toString() method of functions, which previously only printed function code but omitted comments and whitespace. ES2019’s toString() keeps comments, Spaces, etc., that is, prints the original code:

function sayHi() {
  /* dog */
  console.log('wangwang');
}

sayHi.toString();  // Will output the same raw code as above
Copy the code

6. catch

Before ES2019, catches took parameters, but many times catch blocks were redundant. Now you can do it without parameters:

/ / ES2019 before
try{... }catch(error) {
   ...
}

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

Vi. New ES11 Features (2020)

1. BigInt

In JavaScript, the numeric type Number is a 64-bit floating-point Number **, so there are limits to the accuracy and scope of the representation. ES2020 adds the BigInt data type, which is the eighth basic type introduced in JavaScript. BigInt can represent an arbitrarily large integer. The syntax is as follows:

BigInt(value);
Copy the code

Where value is the value to create the object. It can be a string or an integer.

In JavaScript, the largest integer that the Number base type can accurately represent is 253. So the early questions are:

let max = Number.MAX_SAFE_INTEGER;    // The maximum safe integer

let max1 = max + 1
let max2 = max + 2

max1 === max2   // true
Copy the code

With BigInt, this problem goes away:

let max = BigInt(Number.MAX_SAFE_INTEGER);

let max1 = max + 1n
let max2 = max + 2n

max1 === max2   // false
Copy the code

The typeof operator can be used to determine if a variable is of type BigInt (return string “BigInt”) :

typeof 1n= = ='bigint'; // true 
typeof BigInt('1') = = ='bigint'; // true 
Copy the code

Can also through the Object. The prototype. The toString method to judge whether a variable for BigInt type (return a string “[Object BigInt]”) :

Object.prototype.toString.call(10n) = = ='[object BigInt]';    // true
Copy the code

Note that BigInt and Number are not strictly equal, but loosely equal:

10n= = =10 // false 
10n= =10  // true 
Copy the code

Number and BigInt can be compared:

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

2. Null value merge operator (??)

When writing code, if an attribute is not null and undefined, then the attribute is obtained, if the attribute is null or undefined, then the default value is taken:

const name = dogName ? dogName : 'default'; 
Copy the code

Can | | to simplify:

const name =  dogName || 'default'; 
Copy the code

But | | writing exist some shortcomings, when dogName 0 or false will walk to the default logic. So ES2020 introduced?? Operator. Only?? Return the value on the right if the left is null or undefined:

const dogName = false; 
const name =  dogName ?? 'default';  // name = false;
Copy the code

3. Optional chain operator (? .).

In the process of development, we often need to get deeper properties, such as system. User. Addr. Province. The name. However, before obtaining the name attribute, it is necessary to judge whether the preceding attribute exists step by step, otherwise an error will be reported:

const name = (system && system.user && system.user.addr && system.user.addr.province && system.user.addr.province.name) || 'default';
Copy the code

To simplify the process, ES2020 introduces the “chain judgment operator”? ., 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 raise an error when a reference is 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.

constname = system? .user? .addr? .province? .name ||'default';
Copy the code

The optional chain operator makes the expression shorter and more concise when trying to access an object property that may not exist. The optional chain operator is also helpful when exploring the contents of an object if it is not certain which attributes must exist.

Optional chains have the following three forms:

a? .[x]/ / is equivalent to
a == null ? undefined: a[x] a? .b()/ / is equivalent to
a == null ? undefined: a.b() a? . ()/ / is equivalent to
a == null ? undefined : a()
Copy the code

This operator solves many problems when developing with TypeScript.

7. New ES12 features (2021)

1. String.prototype.replaceAll()

The replaceAll() method returns a new string, and all characters that match the rules are replaced, either by strings or regular expressions.

let string = 'hello world, hello ES12'
string.replace(/hello/g.'hi')    // hi world, hi ES12
string.replaceAll('hello'.'hi')  // hi world, hi ES12
Copy the code

Note that replaceAll will throw an exception if it is not a global match (/g) when using regular expressions:

let string = 'hello world, hello ES12'
string.replaceAll(/hello/.'hi') 
// Uncaught TypeError: String.prototype.replaceAll called with a non-global
Copy the code

2. Number delimiter

Number separators create visual separators between numbers, which are separated by _ underscores to make them more readable, and can be placed anywhere within numbers:

const money = 1 _000_000_000
/ / equivalent to the
const money = 1000000000
Copy the code

This new feature also supports octal numbers:

const number = 0o123_456
/ / equivalent to the
const number = 0o123456
Copy the code

3. Promise.any

Promise.any is a new feature in ES2021 that accepts a Promise iterable (such as an array), and as soon as one of the promises succeeds, If no promise in the iterable succeeds (i.e. promises fail/reject), return an instance of the failed promise and AggregateError type, which is a subclass of Error. Used to group single errors together

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.resolve('result'),]Promise.any(promises).then((value) = > {
  console.log('value: ', value)
}).catch((err) = > {
  console.log('err: ', err)
})

// Output: value: result
Copy the code

If every promise that comes in fails:

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.reject('ERROR C'),]Promise.any(promises).then((value) = > {
  console.log('value:, value)
}).catch((err) = > {
  console.log('err:', err)
  console.log(err.message)
  console.log(err.name)
  console.log(err.errors)
})
Copy the code

Output result:

Err: AggregateError: All promises were Rejected All Promises were Rejected AggregateError"ERROR A"."ERROR B"."ERROR C"]
Copy the code