The purpose of this paper is to lay a firm foundation for almost all the knowledge from ES2015 to ES2021, which are extracted from the two knowledge lines of Ruan Yi-feng’s ECMAScript 6 Introduction and TC39-Finished – Proposals. Basically, they are presented in the form of a knowledge point with a piece of code, so the length is relatively long. It is precisely because of the long length that the proposals for Stage 2 and Stage 3 are not written here. I will update them synchronously after ES2021 updates.

Five proposals have been listed in Expected Publication Year in 2021, so we will classify them as ES2021.

Due to space constraints, I split the post into two parts. For the full version, you can check out my Github blog

  • Advice Collection gives you a well-summarized 30,000-word practical guide to ES6 (part 1)
  • A well-summarized 30,000-word practical guide to ES6 (Part 2)

The foreword ES6

The history of

It’s important to be able to write JS well, but as a front end, we also need to understand the evolution of the language we use. JavaScript 20 Years is highly recommended for this book, which Chronicles the development of the language from its inception in 1995 to the ES6 specification in 2015. A total of 20 years of JavaScript evolution.

Release notes

In 2011, ECMAScript 5.1 was released, and the first version of ES6, also known as ES2015, was released in June 2015. ES6 is actually a general term, referring to the next generation of standards after version 5.1. The official version of TC39 is issued in June every year. The version number is based on the year of the current year. For example, ES2015, ES2016, ES2017, ES2018, ES2019, ES2020 and other versions have been released.

Proposal Release Process

Anyone can make a proposal to TC39 to change the language standards. From proposal to formal standard, a new grammar goes through five stages. Changes in each phase will need to be approved by the TC39 Committee.

  • Stage 0Strawperson(Presentation stage)
  • Stage 1Proposal(Comment Stage)
  • Stage 2Draft(Draft stage)
  • Stage 3Candidate(Candidate stage)
  • Stage 4Finished(Finalization stage)

Once a proposal makes it to Stage 2, it will almost certainly be included in the formal standards. All of ECMAScript’s current proposals can be viewed here in ECMA262. More detailed information about the proposal process can be found here at TC39_Process.

ES2015

The statement

Const: declares a constant, let: declares a variable; Const /let declared constants/variables can only be used in a block of code (block-level scope or function scope);

if (true) {
    let name = '布兰'
}
console.log(name)  // undefined
Copy the code

Const /let does not have a raise, so it must be declared in the block before it can be used. This is called a temporary dead zone.

let name = 'bubuzou'
if (true) {
    name = '布兰'
    let name
}
console.log(name)
Copy the code

Const /let does not allow repeated declarations within the same scope;

function setName(name) {
    let name = ' '  // SyntaxError
}
Copy the code

Const must be initialized and cannot be modified, but if an object is initialized, the memory address of the object cannot be modified.

const person = {
    name: '布兰'
}
person.name = 'bubuzou'  
console.log(person.name)  // 'bubuzou'
person = ' '  // TypeError
Copy the code

Const /let const/ variable declared in global scope does not hang in the properties of the top-level object (window in the browser);

var name = '布兰'
let age = 12
console.log(window.name)  // 'Bran'
console.log(window.age)  // undefined
Copy the code

Deconstruction assignment

Deconstruction types:

  • String deconstruction

    let [a, b, c = 'c'] = '12'
    console.log(a, b, c)  // '1' '2' 'c'
    Copy the code
  • Numerical deconstruction

    let {toFixed: tf} = 10
    console.log( tf.call(Math.PI, 2))/ / 3.14
    Copy the code
  • Boolean value deconstruction

    let {toString: ts} = true
    console.log( ts.call(false))// 'false'
    Copy the code
  • Array destruct: The data on the right side of the equal sign has an Iterator interface that can be assigned to the destruct value in array form;

    // Failed to deconstruct the variable with undefined
    let [a, b, c] = [1.2]
    console.log(a, b, c)  // 1, 2, undefined
    
    // You can set default values
    let [x, y, z = 3] = [1.2.null]
    console.log(x, y, z)  // 1, 2, null
    Copy the code

    What kind of data has an Iterator interface? An object is iterable if it can be accessed through [Symbol. Iterator] and returns an object that conforms to the iterator protocol. The current built-in iterables include String, Array, TypeArray, Map, Set, Arguments, and NodeList.

  • Object destructuring: Unlike array destructuring by index position, object destructuring assigns values by property name. If the current object property does not match successfully, the object’s prototype property will be looked up:

    // Default
    let { name: name, age: age } = { name: '布兰'.age: 12 }
    Copy the code
    / / short
    let { name, age } = { name: '布兰'.age: 12 }
    Copy the code
    // Rename and set the default values
    let { name: name1, age: age1 = 12 } = { name: '布兰' }
    console.log(name1, age1)  // 'Bran' 12
    Copy the code
  • Function argument destruct: actually use the above object destruct and array destruct rules;

    function move({x = 0, y = 0} = {}) {
        console.log([x, y])
        return [x, y];
    }
    move({x: 3.y: 8})  / / [3, 8]
    move({x: 3})        / / [3, 0]
    move({})            / / [0, 0]
    move()              / / [0, 0]
    Copy the code

Key points of deconstruction:

  • As long as the pattern on both sides of the equals sign is the same (object or array), the variable on the left is assigned its corresponding value;
  • The value of the variable that fails to be deconstructed isundefined;
  • The default value is valid only if all the values on the right-hand side of the equal sign are undefined;
  • As long as the value on the right-hand side of the equals sign is not an object or an array, it will be automatically boxed into an object;
  • nullundefinedThey can’t be converted to objects, so we can’t deconstruct them.

Deconstructing applications:

  • Exchange the value of a variable;

    let x = 1, y = 2;
    [x, y] = [y, x]
    console.log(x, y)  1 / / 2
    Copy the code
  • Returns object properties through functions

    function getParams() {
        return {
            name: '布兰'.age: 12,}}let {name, age} = getParams()
    Copy the code
  • Variables are declared by defining function arguments

    let person = {
        name: '布兰'.age: 12
    }
    init(person)
    
    // Common usage
    function init(person) {
        let {name, age} = person
    }
    
    // More concise usage
    function init({name, age}) {}
    Copy the code
  • Specify default values for function arguments

    function initPerson({name = '布兰', age = 12} = {}) {
        console.log(name, age)
    }
    initPerson()  // 'Bran' 12
    initPerson({age: 20})  // 'Bran' 20
    Copy the code
  • Extracting JSON data

    let responseData = {
        code: 1000.data: {},
        message: 'success'
    }
    
    let { code, data = {} } = responseData
    Copy the code
  • Traverse the Map structure

    let map = new Map()
    map.set('beijing'.'Beijing')
    map.set('xiamen'.'xiamen')
    
    for (let [key, value] of map) {
        console.log(key, value)
    }
    Copy the code
  • Input the specified methods and properties of the module

    const { readFile, writeFile } = require("fs")
    Copy the code

String extension

  • You can use Unicode encoding to represent a character:

    // The following can be used to represent the character z
    '\z'      / / escape
    '\ 172'    // Decimal notation
    '\x7A'    // Hexadecimal notation
    '\u007A'  // Unicode plain notation
    '\u{7A}'  // Unicode braces notation
    Copy the code

    www.52unicode.com is a website where you can find Unicode encodings for common symbols.

  • You can use for… Of iterates over a string correctly:

    let str = '😀 🤣 😜 😍 🤗 🤔'
    for (const emoji of str) {
        console.log(emoji) / / 😀 🤣 😜 😍 🤗 🤔
    }
    for(let i = 0, l = str.length; i < l; i++) {
        console.log(str[i])  // Cannot output the expression correctly
    }
    Copy the code
  • Template strings are marked with two backquotes (‘ ‘), which can be used to define multi-line strings or to insert variables into strings:

    let name = 'hero'
    let tips = `Hello ${name}, 
        welcome to my world.`
    alert( tips )
    Copy the code
  • Tag template: Following the name of a function with a template string is equivalent to calling the function with an argument:

    let name = '布兰', age = 12;
    let tips = parse`Hello ${name}, are you ${age}years old this year? `
    function parse(stringArr, ... variable) {}The parse function is called by passing the following parameters
    parse(["Hello ".", are you "." years old this year?"], name, age)
    Copy the code
  • String.fromcodepoint () is used to return the corresponding character from a Unicode code point and can support a code point of 0xFFFF:

    String.fromCharCode(0x1f600)   / / ""
    String.fromCodePoint(0x1f600)  / / "😀"
    Copy the code
  • String.raw() returns the result of replacing all the variables in the String and escaping the slash:

    String.raw`Hi\nThe ${2+3}! `  // "Hi\n5!"
    Copy the code
  • CodePointAt () returns the decimal point of the character. For Unicode characters greater than 0xFFFF, it is considered to be 2 characters. The decimal point can be converted to hexadecimal using toString(16) :

    let emoji = '🤣'
    emoji.length  / / 2
    emoji.charCodeAt(0).toString(16)  // 'd83d'
    emoji.charCodeAt(1).toString(16)  // 'de00'
    
    String.fromCodePoint(0xd83d.0xde00) = = ='🤣'  // true
    Copy the code
  • The normalize() method normalizes the current string to a specified Unicode normal form. (If the value is not a string, it is first converted to a string) :

    let str1 = '\u00F1'
    let str2 = '\u006E\u0303'
    
    str1  / / n
    str2  / / n
    str1 === str2  // false
    str1.length === str2.length  // false
    str1.normalize() === str2.normalize()  // true
    Copy the code
  • Whether the string contains substrings:

    • Includes () : Returns a Boolean value indicating whether the parameter string was found.
    • StartsWith () : Returns a Boolean value indicating whether the argument string is at the head of the original string.
    • EndsWith () : Returns a Boolean value indicating whether the argument string is at the end of the original string.
    let s = 'Hello world! '
    
    s.includes('o')        // true
    s.startsWith('Hello')  // true
    s.endsWith('! ')        // true
    Copy the code

    All three methods support a second argument, which indicates where to start the search:

    let s = 'Hello world! '
    
    s.includes('Hello'.6)   // false
    s.startsWith('world'.6) // true
    s.endsWith('Hello'.5)   // true
    Copy the code

    The above code indicates that endsWith behaves differently from the other two methods when the second parameter n is used. It is for the first n characters, while the other two methods are for the NTH position until the end of the string.

  • Repeat (n) returns a new string after repeating the current string n times:

    'x'.repeat(2)         // 'xx'
    'x'.repeat(1.9)       // 'x'
    'x'.repeat(NaN)       / /"
    'x'.repeat(undefined) / /"
    'x'.repeat('2a')      / /"
    'x'.repeat(-0.6)      The decimal between 0 and 1 is equal to 0
    'x'.repeat(-2)        // RangeError
    'x'.repeat(Infinity)  // RangeError
    Copy the code

Numerical extension

  • Binary (0b) and octal (0o) notation:

    let num = 100
    let b = num.toString(2)  // Binary 100:1100100
    let o = num.toString(8)  // Octal 100:144
    0b1100100= = =100        // true
    0o144= = =100            // true
    Copy the code
  • Number.isfinite () determines if a Number isFinite, and returns false if it is not:

    Number.isFinite(-2.9)      // true
    Number.isFinite(NaN)       // false
    Number.isFinite(' ')        // false
    Number.isFinite(false)     // true
    Number.isFinite(Infinity)  // false
    Copy the code
  • Number.isnan () determines if a value isNaN, and if the input is not NaN then the result is false:

    Number.isNaN(NaN)       // true
    Number.isFinite('a'/0)  // true
    Number.isFinite('NaN')  // false
    Copy the code
  • Numeric conversion: number.parseint () and number.parsefloat (), non-strict conversion, parses the string left to right, stops parsing if it is not a Number, and returns the parsed Number:

    parseInt('12a')  / / 12
    parseInt('a12')  // NaN
    parseInt(' ')  // NaN
    parseInt('0xA')  // 10,0, x is treated as a hexadecimal number
    Copy the code

    ParseInt () parseInt() parseInt() parseInt() parseInt() parseInt() parseInt() parseInt() parseInt() parseInt() parseInt() parseInt() parseInt() parseInt() parseInt();

    parseInt('1010'.2)  / / 10
    parseInt('ff'.16)  / / 255
    Copy the code

    Reference: parseInt

  • Number.isinteger () determines whether a value is an integer, and returns false if the incoming argument is non-numeric:

    Number.isInteger(25)   // true
    Number.isInteger(25.0) // true
    Number.isInteger()     // false
    Number.isInteger(null) // false
    Number.isInteger('15') // false
    Number.isInteger(true) // false
    Number.isInteger(3.0000000000000002) // true
    Copy the code

    If data precision is required, you are not advised to use number.isinteger () to check whether a value is an integer.

  • Number.EPSILON represents a minimum acceptable error range, usually used for floating-point operations:

    0.1 + 0.2= = =0.3  // false
    Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON  // true
    Copy the code
  • Number.issafeinteger () is used to determine whether a Number is between the maximum safe integer (number.max_safe_INTEGER) and the minimum safe integer (number.min_safe_INTEGER) :

    Number.MAX_SAFE_INTEGER === 2支那53 -1        // true
    Number.MAX_SAFE_INTEGER === 9007199254740991  // true
    Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER  // true
    Number.isSafeInteger(2)         // true
    Number.isSafeInteger('2')       // false
    Number.isSafeInteger(Infinity)  // false
    Copy the code
  • Math.trunc() : Returns the integer part of the value

  • Math.sign() : Return numeric type (positive 1, negative -1, zero 0)

  • Math.cbrt() : Returns a numeric cube root

  • Math.clz32() : Returns a 32-bit unsigned integer for a value

  • Math.imul() : Returns the multiplication of two values

  • Math.fround() : Returns a 32-bit single-precision floating-point form of a value

  • Math.hypot() : Returns the square root of all numeric sums of squares

  • Math.expm1() : returns e^ n-1

  • Math.log1p() : Returns the natural logarithm of 1 + n (math.log (1 + n))

  • Math.log10() : Returns the logarithm base 10 of n

  • Math.log2() : Returns the logarithm base 2 of n

  • Math.sinh() : Returns the hyperbolic sine of n

  • Math.cosh() : Returns the hyperbolic cosine of n

  • Math.tanh() : Returns the hyperbolic tangent of n

  • Math.asinh() : Returns the inverse hyperbolic sine of n

  • Math.acosh() : Returns the inverse hyperbolic cosine of n

  • Math.atanh() : Returns the inverse hyperbolic tangent of n

Array extension

  • Array extension operator (…) To expand an array into a comma-separated sequence of arguments, expand only one level of the array:

    // Application 1: function pass parameters
    Math.max(... [1.2.3])  / / 3
    
    // Application 2: array merge
    let merge = [...[1.2], ...[3.4].5.6]  // 1, 2, 3, 4, 5, 6
    
    // Application 3: shallow cloning
    let a = [1.2.3]
    let clone = [...a]
    a === clone  // false
    
    // Application 4: array destruct
    const [x, ...y] = [1.2.3]
    x  / / 1
    y  / / [2, 3]
    Copy the code
  • Array.from() converts array-like objects (NodeList, arguments) and iterables into arrays:

    // Application 1: string to array
    Array.from('foo')  // ['f', 'o', 'o']
    
    // Application 2: array merge deduplicate
    let merge = [...[1.2], ...[2.3]]
    Array.from(new Set(merge))  / / [' 1 ', '2', '3']
    
    // Use arguments to convert the array
    function f() {
        return Array.from(arguments)
    }
    f(1.2.3)  / / [1, 2, 3]
    Copy the code

    If array.from () takes a second argument, mapFn, a map operation is performed on the generated Array:

    Array.from([1.2.3].(x) = > x * x )    / / [1, 4, 9]
    Array.from({length: 3}, (v, i) = > ++i)  / / [1, 2, 3]
    Copy the code
  • Array.of() turns a set of arguments into an Array:

    Array.of(1.2.3)  / / [1, 2, 3]
    
    / / similar to
    function arrayOf(. params){
        return [].slice.call(params)
    }
    arrayOf(1.2.3)  / / [1, 2, 3]
    Copy the code
  • Array.copywithin () copies the members at the specified location to other locations (overwriting the members at the original location) within the current Array, and returns a new Array. Receive three parameters, and a negative parameter indicates that the calculation starts on the right:

    • target(Mandatory) : Index of the replacement location;
    • start(Optional) : Data is read from this position. The default value is 0.
    • end(Optional) : The data is read from the end of this position (excluding the data at this position). The default is the original array length.
    [1.2.3.4.5].copyWithin(-1)         // [1, 2, 3, 4, 1]
    [1.2.3.4.5].copyWithin(1)          // [1, 1, 2, 3, 4]
    [1.2.3.4.5].copyWithin(0.3.4)    // [4, 2, 3, 4, 5]
    [1.2.3.4.5].copyWithin(0, -3, -1)  // [3, 4, 3, 4, 5]
    Copy the code
  • Find the first child member that appears: find() and findIndex() :

    // Find the first even number
    [1.6.9].find((val, index, arr) = > val % 2= = =0)       / / 6
    
    // Find the index position of the first even number
    [1.6.9].findIndex((val, index, arr) = > val % 2= = =0)  / / 1
    Copy the code
  • Fill () fills the array with the given value and takes three arguments:

    • value: fill in the value;
    • start(Optional), start index, default is 0;
    • end(Optional) : End index. The default value is the length of the array, excluding the value of the index position.
    // Initialize an empty array
    Array(3).fill(1)  / / [1, 1, 1)
    
    [1.2.3.4].fill('a'.2.4)  // [1, 2, 'a', 'a']
    Copy the code
  • To get an array iterator object, go to keys() (key name), entries() (key value), and values() (key-value pairs). Of iterations,

    let arr = ['a'.'b'.'c']
    for (let x of arr.keys()) {
        console.log(x)  / / 1, 2, 3
    }
    for (let v of arr.values()) {
        console.log(v)  // 'a' 'b' 'c'
    }
    for (let e of arr.entries()) {
        console.log(e)  // [0, 'a'] [0, 'b'] [0, 'c']
    }
    Copy the code
  • An array void is an exponential group with no value, such as [,,], whereas [undefined] contains no void. Since some of the previous ES6 apis had very different rules about how to handle vacancies, you should try to avoid them. To change this, the ES6 API will default to undefined:

    [[...1.3].values()]  // [1, undefined, 3]
    [1.3].findIndex(x= > x === undefined)  / / 1
    Copy the code

Object extension

  • Shorthand for object properties:

    let name = '布兰'
    let person = {
        name,
        getName() {
            return this.name
        }
    }
    / / is equivalent to
    let person1 = {
        name: '布兰'.getName: function() {
            return this.name
        }
    }
    Copy the code
  • Property name expressions: When you define an object using an object literal, you are allowed to define an object property using a property name expression:

    let name = 'name'.let person = {
        [name]: '布兰'['get'+ name](){
            return this.name
        }
    }
    Copy the code
  • Method name attribute, there are several cases, here are only a few common ones:

    In case 1, the name attribute of a normal object method returns the name of the method directly, so does a function declaration, and a function expression returns the variable name:

    
    let person = {
        hi(){}
    }
    person.hi.name  // 'hi'
    Copy the code

    Case 2: Constructor name is anonymous:

    (new Function).name  // 'anonymous'
    Copy the code

    Case 3: The name of the bound function will precede the function name with bound:

    function foo() {} 
    foo.bind({}).name  // 'bound foo'
    Copy the code

    Case 4: If an object’s method uses a getter and setter, the name property is not on the method, but on the get and set properties of the object that describes the method’s property:

    let o = { 
        get foo() {},set foo(x){} 
    }
    o.foo.name  // TypeError: Cannot read property 'name' of undefined
    let descriptor = Object.getOwnPropertyDescriptor(o, "foo") 
    descriptor.get.name  // "get foo" 
    descriptor.set.name  // "set foo"
    Copy the code

    Reference: function_name

  • Attribute enumerability

    Each property of an object has a Descriptor that controls the behavior of that property. Through Object. GetOwnPropertyDescriptor Object () to obtain a property description:

    let person = { name: '布兰'.age: 12 }
    Object.getOwnPropertyDescriptor(person, 'name')
    / / {
    // configurable: true,
    // enumerable: true,
    // value: "bran ",
    // writable: true,
    // }
    Copy the code

    Enumerable is an enumerable property of an object. If enumerable is false, the property cannot be enumerated, so it is ignored in one of the following operations:

    • for... in: Only iterates over the object’s own and inherited enumerable properties;
    • Object.keys(): returns the keys of all the enumerable properties of the object itself;
    • JSON.stringify(): serializes only the enumerable properties of the object itself;
    • Object.assign(): Copies only the enumerable properties of the object itself.
    let person = { name: '布兰' }
    Object.defineProperty(person, 'age', {
        configurable: true.enumerable: false.value: 12.writable: true
    })
    person  // {name: 'bran ', age: 12}
    
    // The following actions will ignore the age property of the Person object
    for (let x in person) {
        console.log(x)         // 'name'
    }
    Object.keys(person)        // ['name']
    JSON.stringify(person)     // '{name": "bran "}'
    Object.assign({}, person)  // { name: '布兰' }
    Copy the code

    Reflect.ownkeys (obj) : Returns an array of all the key names of the object itself (not inherited), whether the key names are Symbol or string, and whether they are enumerable or not:

    // Based on the above code
    Reflect.ownKeys(person)  // ['name', 'age']
    Copy the code
  • The super keyword, which points to the object’s prototype object, can only be used in the object’s methods, and will report an error elsewhere:

    let person = {
        name: '布兰'.getName() {
            return super.name
        }
    }
    Object.setPrototypeOf(person, {name: 'hello'})
    person.getName()  // 'hello'
    
    // An error will be reported when using the following super
    const obj1 = {
        foo: super.foo
    }
    const obj2 = {
        foo: () = > super.foo
    }
    const obj3 = {
        foo: function () {
            return super.foo
        }
    }
    Copy the code
  • Object.is() is used to determine whether two values are equal, and is basically the same as ===, except in the following cases:

    +0= = = -0            //true
    NaN= = =NaN          // false
    Object.is(+0, -0)    // false
    Object.is(NaN.NaN)  // true
    Copy the code
  • Object.assign() is used to merge objects and copy all enumerable properties of the source Object to the target Object. If there are properties with the same name, the following properties will be replaced directly:

    let target = { a: 1 }
    let source1 = { a: 2.b: 3.d: {e: 1.f: 2}}let source2 = { a: 3.c: 4.d: {g: 3}}Object.assign(target, source1, source2)
    target  // { a: 3, b: 3, c: 4, d: {g: 3} }
    Copy the code

    Object.assign() performs a shallow copy. If a property of the source Object is an Object, then a reference to that Object is copied:

    let target = {a: {b: 1}}
    let source = {a: {b: 2}}
    Object.assign(target, source)
    target.a.b = 3
    source.a.b  / / 3
    Copy the code
  • The __proto__ attribute is used to read and set the prototype of the current object, and since the underscore is more superficial and is an internal attribute, it is not recommended to use it in formal situations. Instead, use the following object.setPrototypeof () (write operation), object.getPrototypeof () (read operation), and object.create () (generate operation).

  • Object.setprototypeof () is used to set the Object stereotype, and object.getPrototypeof () is used to read the Object stereotype:

    let person = {name: '布兰'}
    Object.setPrototypeOf(person, {name: 'animals'})
    Object.getPrototypeOf(person)  // {name: 'animal '}
    Copy the code

Regular extension

  • The RegExp constructor allows the first argument to be a regular expression and the second argument to be a modifier. If there is a second argument, the second modifier takes precedence:

    let reg = new RegExp(/xYz\d+/gi, i)
    reg.flags  // 'i'
    Copy the code
  • Regular method call changes: match(), replace(), search(), split() internal calls on string objects are converted to call RegExp. Prototype [Symbol. Method] for RegExp instance;

  • U modifier: means Unicode mode and is used to properly handle Unicode characters greater than \uFFFF. That is, if the string to be matched may contain characters greater than \uFFFF, the u modifier must be added for proper processing.

    // Add the u modifier to make the. Character correctly recognize characters greater than \uFFFF
    /^.$/.test('🤣')   // false
    /^.$/u.test('🤣')  // true
    
    // The u modifier must be added to the bracketed Unicode character notation
    /\u{61}/.test('a')   // false
    /\u{61}/u.test('a')  // true
    
    // Only with the u modifier can the quantifier correctly match characters greater than \uFFFF/ 🤣 {2}/.test('🤣 🤣')  // false/ 🤣 {2}/u.test('🤣 🤣') // true
    Copy the code

    The regexp.prototype. unicode property indicates whether the re has the u modifier set:

    / 🤣 / unicode// false/ 🤣 u.u nicode// true
    Copy the code
  • The y modifier, like the g modifier, is also a global match; The difference is that g matches the rest of the characters, while y matches the first of the remaining characters, so the y modifier is also called the adhesive modifier:

    let s = 'aaa_aa_a'
    let r1 = /a+/g
    let r2 = /a+/y
    
    r1.exec(s)  // ["aaa"]
    r2.exec(s)  // ["aaa"]
    
    r1.exec(s)  // ["aa"]
    r2.exec(s)  // null
    Copy the code

    The regexp.prototype. sticky property indicates whether the y modifier is set:

    /abc/y.sticky  // true
    Copy the code
  • The regexp.prototype. flags property returns all modifiers for the current regular:

    / ABC 🤣 iuy. Flags// 'iuy'
    Copy the code

Function extension

  • Default values for function arguments. Arguments must not have the same name, and the body of a function must not declare variables with the same name as let and const:

    function printInfo(name = '布兰', age = 12) {}
    Copy the code

    When using the default parameter value, the parameter must not have the same name:

    function f(x, x, y) {}      / / is not an error
    function f(x, x, y = 1) {}  / / an error
    Copy the code

    We cannot declare variables with the same argument name in a function body as let and const:

    / / an error
    function f(x, y) {
        let x = 0
    }
    Copy the code

    The length attribute of the function returns the number of arguments that have no default value, and if the default value is set to an argument that is not a tail argument, length is not included in the following arguments:

    (function f(x, y){}).length      / / 2
    (function f(x, y = 1){}).length  / / 1
    (function f(x = 1, y){}).length  / / 0
    Copy the code
  • Remaining (REST) parameters (… To get the rest of the arguments to the function, note that the REST argument must be placed last. This is a good substitute for the arguments object:

    function f(x, ... y) {
        console.log(x)  / / 1
        for (let val of y) {
            coonsole.log(val)  / / 2, 3
        }
    }
    f(1.2.3)
    Copy the code
  • Strict mode: As long as the function parameter USES the default values, deconstruction, assignment or extension operators so functions in the body can’t set the display as strict mode, because the scope of strict mode contains function parameters, and the order of the function is performed first parameter, and then execute the body of the function, implementation to use in the function body strict, Now that the function arguments have been executed, do they need to be restricted by a strict pattern? There is a contradiction. There are two ways to circumvent this restriction: set strict mode globally or package an immediate execution function outside the function body and declare strict mode:

    / / solution
    'use strict'
    function f(x, y = 2) {}/ / solution 2
    let f = (function(){
        'use strict'
        return function(x, y = 2) {}
    })()
    Copy the code
  • Arrow function syntax is more concise than function expressions and does not have its own this, arguments, and cannot be used as constructors and generators.

    How to write arrow function:

    let f1 = () = > {}               // No arguments
    let f2 = (x) = > {}              // One parameter
    let f3 = x= > {}                // 1 parameter can omit parentheses
    let f4 = (x, y) = > {}           // More than 2 parameters must be enclosed in parentheses
    let f5 = (x = 1, y = 2) = > {}   // Default parameter values are supported
    let f6 = (x, ... y) = > {}        // Support rest parameters
    let f7 = ({x = 1, y = 2} = {})  // Support parameter deconstruction
    Copy the code

    Arrow functions do not have their own this:

    function Person(){
        this.age = 0
        setInterval(() = > {
            this.age++
        }, 1000)}var p = new Person()  Person {age: 1}
    Copy the code

    Call /apply calls to arrow functions will not bind to the scope of the first argument:

    let adder = {
        base: 1.add: function(a) {
            let f = v= > v + this.base
            return f(a)
        }, 
        addThruCall: function(a) {
            let f = v= > v + this.base
            let b = {
                base: 2
            } 
            return f.call(b, a)
        }
    }
    adder.add(1)          2 / / output
    adder.addThruCall(1)  // Still output 2
    Copy the code

    The arrow function does not have its own arguments object, but you can use rest arguments instead:

    let log = () = > {
        console.log(arguments)  // ReferenceError
    }
    log(2.3)
    
    // The rest of the parameters instead of writing
    let restLog = (. arr) = > {
        console.log(arr)  / / [2, 3]
    }
    restLog(2.3)
    Copy the code

    The arrow function cannot be used as a constructor, and when used with new it throws an error:

    let Foo = () = > {}
    let foo = new Foo()
    // TypeError: Foo is not a constructor
    Copy the code

    Arrow functions return object literals, enclosed in parentheses:

    let func2 = () = > ({foo: 1})
    Copy the code

    Reference: Arrow_functions

  • Tail calls and tail recursion

    First, we need to know what a tail call is: the last step of a function calling another function:

    // Is a tail call
    function f(x) {
        return g(x)
    }
    
    // None of the following are tail calls
    function f(x) {
        let y = g(x)
        return y
    }
    function f(x) {
        let y = g(x)
        return g(x) + 1
    }
    function f(x) {
        g(x)
        // Because the last step is return: undefined
    }
    Copy the code

    What’s the use of tail calls? We know that functions calling each other will generate “call frames”, which store information such as the internal variables of the function and the location of the calling function. All of these “call frames” form a “call stack”. If you call another function in the last step of the operation, you don’t need to keep the “call frame” of the outer function because the information about the call position, internal variables, and so on is no longer needed. You can simply replace the “call frame” of the outer function with the “call frame” of the inner function:

    function f() {
        let m = 1
        let n = 2
        return g(m + n)
    }
    f()
    
    / / is equivalent to
    function f() {
        return g(3)
    }
    f()
    
    / / is equivalent to
    g(3)
    Copy the code

    This significantly reduces the number of frames in the call stack and reduces the memory footprint, so this is the tail call optimization. The same is true of tail recursion. If recursion is repeated many times, it will need to keep a lot of “call frames”, so it will often cause stack overflow errors, which will not occur with tail recursion optimization:

    // Regular recursive Fibonacci functions
    function Fibonacci(n) {
        if ( n <= 1 ) {return 1}
        return Fibonacci(n - 1) + Fibonacci(n - 2)
    }
    Fibonacci(100) / / timeout
    
    // Tail-recursively optimized Fibonacci function
    function Fibonacci2(n, ac1 = 1, ac2 = 1) {
        if( n <= 1 ) {return ac2}
        return Fibonacci2(n - 1, ac2, ac1 + ac2)
    }s
    Fibonacci2(100)  / / 573147844013817200000
    Copy the code

Symbol

  • Symbol is a new primitive type that represents a unique value. You can create a value of type Symbol using the Symbol() function. To distinguish it, you can pass a string describing it:

    let s1 = Symbol('foo') 
    let s2 = Symbol('foo')
    s1 === s2  // false
    Copy the code
  • The Symbol type cannot be implicitly converted using mathematical operators, but can be converted to a String by displaying String() or to a Boolean by displaying Boolean() :

    let s = Symbol('foo')
    String(s)     // "Symbol('foo')"
    s.toString()  // "Symbol('foo')"
    Boolean(s)    // true
    Copy the code
  • The main purpose of introducing Symbol is to use it as the name of an object’s property. This avoids conflicting attribute names:

    let foo = Symbol('foo')
    let obj = {
        [foo]: 'foo1'.foo: 'foo2'
    }
    obj[foo]  // 'foo1'
    obj.foo   // 'foo2'
    Copy the code
  • The Symbol attribute is not enumerable, and will not be used for… In, for… Of, Object. Keys (), Object. GetOwnPropertyNames (), JSON. The stringify () enumeration:

    let person = {
        name: '布兰'[Symbol('age')]: 12,}for (let x in person) {
        console.log(x)  // 'name'
    }
    Object.keys(person)  // ['name']
    Object.getOwnPropertyNames(person)  // ['name']
    JSON.stringify(person)  // '{name":" bran "}'
    Copy the code

    But can be by Object. GetOwnPropertySymbols () to obtain all the Symbol to the Object attribute name, returns an array:

    // Based on the above code
    Object.getOwnPropertySymbols(person)  // [Symbol(age)]
    Copy the code

Static methods:

  • Symbol. For () looks for a Symbol globally as described, and registers one globally if no Symbol is found:

    let s1 = Symbol.for('foo')
    let s2 = Symbol.for('foo')
    s1 === s2 // true
    Copy the code

    The global registry feature of symbol.for () can be used to retrieve the same value from different iframes or service workers.

  • Symbol.keyfor () looks up a description of a globally registered Symbol:

    let s = Symbol.for('foo')
    Symbol.keyFor(s)  // 'foo'
    Copy the code

Built-in values of Symbol:

  • Symbol.hasInstance: points to an internal method that is used by other objectsinstanceofOperator to determine whether there is an instance of this object;
  • Symbol.isConcatSpreadable: refers to a Boolean that defines an object forArray.prototype.concat()Can be expanded when;
  • Symbol.species: refers to a constructor that is called when an instance object uses its own constructor;
  • Symbol.match: refers to a function when the instance object isString.prototype.match()When called, the behavior of match() is redefined;
  • Symbol.replace: refers to a function when the instance object isString.prototype.replace()It will be redefined when calledreplace()Behavior;
  • Symbol.search: refers to a function when the instance object isString.prototype.search()It will be redefined when calledsearch()Behavior; s
  • Symbol.split: refers to a function when the instance object isString.prototype.split()It will be redefined when calledsplit()Behavior;
  • Symbol.iterator: points to a default traverser method when the instance object executesfor... ofThe specified default traverser is called;
  • Symbol.toPrimitive: refers to a function that returns the primitive type value of an instance object when it is converted to that primitive type;
  • Symbol.toStringTag: refers to a function when the instance object isObject.prototype.toString()When called, the return value appears intoString()The returned string represents the type of the object;
  • Symbol.unscopables: Points to an object, specifying the use ofwithWhich properties will bewithEnvironmental elimination;

Set

  • A Set is a new data structure, like an array, but it has no keys, only values, and the values are all unique. The constructor can generate a new instance, taking an array or an iterable data structure as an argument:

    new Set([1.2.3])  // Set {1, 2, 3}
    new Set('abc')      // Set {'a', 'b', 'c'}
    Copy the code
  • Evaluezero === === === === === === === === ===

    let set = new Set(a)let a = NaN
    let b = NaN
    set.add(a)
    set.add(b)
    set.size  / / 1
    Copy the code
  • Different instances of the same object are also considered unequal by a Set:

    let set = new Set(a)let a = {a: 1}
    let b = {a: 1}
    set.add(a)
    set.add(b)
    set.size  / / 2
    Copy the code
  • The Set is sequential and will iterate in the order in which it was inserted. You can use for… Of iterations:

    let set = new Set([1.3])
    set.add(5)
    set.add(7)
    for(let x of set) {
        console.log(x)
    }
    Copy the code

Set instance properties and methods:

  • Set.prototype.constructor: constructor, which defaults toSetFunctions;
  • Set.prototype.sizeReturns theSetThe total number of members of the instance;
  • Set.prototype.add(value): Adds a value and returnsSetThe structure itself;
  • Set.prototype.delete(value): deletes a value and returns a Boolean value to indicate whether the deletion is successful.
  • Set.prototype.has(value): Returns a Boolean value indicating whether the value is a member of a Set;
  • Set.prototype.clear(): clears all members, no return value;
  • Set.prototype.keys(): iterator that returns the key name;
  • Set.prototype.values(): the iterator that returns the key value;
  • Set.prototype.entries(): iterator that returns a key-value pair;
  • Set.prototype.forEach(): iterates over each member using a callback function;
let set = new Set([1.3])
set.add(5)     // Set {1, 3, 5}
set.size       / / 3
set.delete(1)  // True, 1 has been deleted
set.has(1)     // false
set.keys()     // SetIterator {3, 5}
set.clear()
set.size       / / 0
Copy the code

Set Application scenario:

  • Array deduplicate:

    [...new Set([1.3.6.3.1]]/ / [1, 3, 6]
    Array.from(new Set([1.3.6.3.1]))  / / [1, 3, 6]
    Copy the code
  • Deduplicate string:

    [...new Set('abcbacd')].join(' ')  // 'abcd'
    Copy the code
  • Find the intersection/union/difference of two sets:

    let a = new Set([1.2.3])
    let b = new Set([4.3.2])
    
    / / and set
    let union = new Set([...a, ...b])  // Set {1, 2, 3, 4}
    
    / / intersection
    let intersect = new Set([...a].filter(x= > b.has(x)))  // set {2, 3}
    
    // The difference set (a vs. B)
    let difference = new Set([...a].filter(x= >! b.has(x)))// Set {1}
    Copy the code
  • Iterating to modify the values of collection members:

    let set = new Set([1.2.3])
    
    / / method
    let set1 = new Set([...set].map(val= > val * 2))     // Set {2, 3, 6}
    
    / / method 2
    let set2 = new Set(Array.from(set, val= > val * 2))  // Set {2, 4, 6}
    Copy the code

WeakSet

  • WeakSet objects allow weak holding objects to be stored in a collection:

    let ws = new WeakSet(a)let foo = {}
    ws.add(foo)  // WeakSet {{}}
    ws.has(foo)  // true
    ws.delete(foo)  // WeakSet {}
    Copy the code

The difference between Set and Set:

  • WeakSetCan only be a collection of objects, not any arbitrary value of any type;
  • WeakSetHolding a weak reference: References to objects in the collection are weak references. If there is no other rightWeakSetObjects in, and those objects are garbage collected. It also means thatWeakSetDoes not store a list of the current object in And because of that,WeakSetIs not enumerable, there is nosizeProperty, noneclearAnd methods of traversal.

Example method:

  • WeakSet.prototype.add(value): Adds a new elementvalue;
  • WeakSet.prototype.delete(value)From this:WeakSetDelete from objectvalueThis element;
  • WeakSet.prototype.has(value): Returns a Boolean value representing the given valuevalueStudent: Does it exist in thisWeakSet;

Map

  • The difference is that the key of an Object can only be a string or Symbol. The key of a Map can be any type (primitive type, Object, or function). An instance of a Map can be created using the Map constructor. The input parameters are data structures with an Iterator interface and each member is a two-element array [key, value] :

    let map1 = new Map()
    map1.set({}, 'foo')
    
    let arr = [['name'.'布兰'], ['age'.12]]
    let map2 = new Map(arr)
    Copy the code
  • The keys in the Map must be unique just like the values in the Set. Follow the sameValueZero algorithm.

    let map = new Map(a)let foo = {foo: 'foo'}
    map.set(foo, 'foo1')
    map.set(foo, 'foo2')
    map.get(foo)  // 'foo2' 
    Copy the code
  • For the same key name as NaN and for different instances of the same object, the treatment is the same as for the Set value:

    let a = NaN
    let b = NaN
    let map = new Map()
    map.set(a, 'a')
    map.set(b, 'b')
    map.size    / / 1
    map.get(a)  // 'b'
    
    let c = {c: 'c'}
    let d = {c: 'c'}
    map.set(c, 'c')
    map.set(d, 'd')
    map.size    / / 3
    map.get(c)  // 'c'
    Copy the code

Instance properties and methods:

  • Map.prototype.sizeReturns theMapNumber of key-value pairs for an object;
  • Map.prototype.set(key, value)Set:MapThe value of the key in the Returns theMapObject;
  • Map.prototype.get(key): Returns the value corresponding to the key, or returns if it does not existundefined;
  • Map.prototype.has(key): returns a Boolean value indicatingMapWhether the instance contains the value corresponding to the key;
  • Map.prototype.delete(key)If:MapObject, it is removed and returnedtrue;
  • Map.prototype.clear()Remove:MapAll key/value pairs of the object;
  • Map.prototype.keys(): Returns a new oneIteratorObject, which contains in the order of insertionMapThe key of each element in the object;
  • Map.prototype.values(): Returns a new oneIteratorObject, which contains in the order of insertionMapThe value of each element in the object;
  • Map.prototype.entries(): Returns a new oneIteratorObject, which contains in the order of insertionMapObject for each element in the[key, value]The array;
  • Map.prototype.forEach(callbackFn[, thisArg]): Traversal in insertion orderMap;
let map = new Map()
map.set({a: 1}, 'a')
map.set({a: 2}, 'b')

for (let [key, value] of map) {
    console.log(key, value)
}
// {a: 1} 'a'
// {a: 2} 'b'

for (let key of map.keys()) {
    console.log(key)
}
// {a: 1}
// {a: 2}
Copy the code

WeakMap

  • Similar to the structure of Map, but the key must be a weak reference to the object, note that the weak reference is the key name rather than the key value, so the WeakMap can not be iterated;

    let wm = new WeakMap(a)let foo = {name: 'foo'}
    wm.set(foo, 'a')  // Weak
    wm.get(foo)       // 'a'
    wm.has(foo)       // true
    Copy the code

    Although wm’s key has a reference to the foo object, it does nothing to prevent the Foo object from being collected by GC. When the reference object foo is garbage collected, the foo key-value pair for WM is automatically removed, so there is no need to manually delete the reference.

Example method:

  • WeakMap.prototype.delete(key)Remove:keyThe associated object of;
  • WeakMap.prototype.get(key): returns the key associated object, or undefined(if there is no key associated object);
  • WeakMap.prototype.has(key): Based on whether there iskeyThe associated object returns oneBooleanValue;
  • WeakMap.prototype.set(key, value)In:WeakMapTo set a group ofkeyAssociated object, returns thisWeakMapObject;

Proxy

  • The custom behavior used by the Proxy to define the basic operation can be understood as interception (executing the method defined in handler) before an operation is performed on the target object. The interception must be performed on the Proxy instance. The interception is not performed on the target object. You can define a proxy instance as follows

    let proxy = new Proxy(target, handler)
    
    let instance = new Proxy({name: '布兰'}, {
        get(target, propKey, receiver) {
            return `hello, ${target.name}`
        },
    })
    instance.name  // 'Hello, Bran'
    Copy the code
  • If handle does not set any interception, then the action on the instance will be forwarded to the target object:

    let target = {}
    let proxy = new Proxy(target, {})
    proxy.name = '布兰'
    target.name  // 'Bran'
    Copy the code
  • When the target object is represented by a Proxy, the internal this refers to the instance of the Proxy:

    const target = {
        m: function () {
            console.log(this === proxy)
        }
    }
    const handler = {}
    const proxy = new Proxy(target, handler)
    target.m()  // false
    proxy.m()   // true
    Copy the code

Static methods:

  • Proxy.revocable() defines a revocable Proxy:

    let target = {}
    let handler = {}
    let {proxy, revoke} = Proxy.revocable(target, handler)
    
    proxy.foo = 123
    proxy.foo  / / 123
    revoke()
    proxy.foo  // TypeError
    Copy the code

Methods for handling objects:

  • get(target, propKey, receiver): Intercepts the reading of object properties, such as proxy.foo and proxy[‘foo’].
  • set(target, propKey, value, receiver): Intercepts the setting of an object property, such asProxy. Foo = v or proxy['foo'] = vReturns a Boolean value.
  • has(target, propKey)Intercept:propKey in proxyReturns a Boolean value.

DeleteProperty (Target, propKey) : Intercepts the delete Proxy [propKey] operation and returns a Boolean value.

  • ownKeys(target)Intercept:Object.getOwnPropertyNames(proxy),Object.getOwnPropertySymbols(proxy),Object.keys(proxy),for... inLoop, returning an array. This method returns the property names of all of the target object’s own properties, andObject.keys()Only the traversable properties of the target object itself are included in the return result of.
  • getOwnPropertyDescriptor(target, propKey)Intercept:Object.getOwnPropertyDescriptor(proxy, propKey)Returns the description object for the property.
  • defineProperty(target, propKey, propDesc)Intercept:Object.defineProperty(proxy, propKey, propDesc),Object.defineProperties(proxy, propDescs)Returns a Boolean value.
  • preventExtensions(target)Intercept:Object.preventExtensions(proxy)Returns a Boolean value.
  • getPrototypeOf(target)Intercept:Object.getPrototypeOf(proxy)Returns an object.
  • isExtensible(target)Intercept:Object.isExtensible(proxy)Returns a Boolean value.
  • setPrototypeOf(target, proto)Intercept:Object.setPrototypeOf(proxy, proto)Returns a Boolean value. If the target object is a function, there are two additional operations that can be intercepted.
  • apply(target, object, args)Intercept:ProxyInstance as a function call, such asproxy(... args),proxy.call(object, ... args),proxy.apply(...).
  • construct(target, args)Intercept:ProxyAn instance of an operation called as a constructor, such asnew proxy(... args).

Reflect

  • Reflect is a built-in object that provides a way to intercept JavaScript operations. These methods are the same as those for proxy Handlers. Reflect is not a function object, so it is not constructable.

  • Purpose of the design:

    • willObjectMethods that are within the language are put intoReflectOn;
    • Modify someObjectMethod to make it more reasonable;
    • letObjectOperations become functional behaviors;
    • Proxy handlesReflectMethods are one-to-one, with the former defining custom behavior and the latter restoring default behavior;

Static methods:

  • Reflect.apply(target, thisArgument, argumentsList)To call a function, you can pass an array as the call argument. andFunction.prototype.apply()Similar function;
  • Reflect.construct(target, argumentsList[, newTarget])On the constructornewTo operate is equivalent to to performnew target(... args);
  • Reflect.defineProperty(target, propertyKey, attributes)Object.defineProperty()Similar. Returns if the setting is successfultrue;
  • Reflect.deleteProperty(target, propertyKey)FunctionallydeleteOperator, which is equivalent to executingdelete target[name];
  • Reflect.get(target, propertyKey[, receiver])Gets the value of a property on an object, similar totarget[name];
  • Reflect.getOwnPropertyDescriptor(target, propertyKey)Similar to theObject.getOwnPropertyDescriptor(). Returns the corresponding attribute descriptor if the attribute exists in the object, otherwiseundefined;
  • Reflect.getPrototypeOf(target)Similar to theObject.getPrototypeOf();
  • Reflect.has(target, propertyKey)Determines whether an object has a property, andinOperators do exactly the same thing;
  • Reflect.isExtensible(target)Similar to theObject.isExtensible();
  • Reflect.ownKeys(target)Returns an array containing all of its own properties (excluding inherited properties). (similar to theObject.keys()But it won’t beenumerableEffects);
  • Reflect.preventExtensions(target)Similar to theObject.preventExtensions(). Returns aBoolean;
  • Reflect.set(target, propertyKey, value[, receiver])A function that assigns a value to a property. Returns aBooleanReturns if the update was successfultrue;
  • Reflect.setPrototypeOf(target, prototype)A function that sets the object prototype. Returns aBooleanReturns if the update was successfultrue;

Class

  • Class keyword can be used to define a class, class is a class of things with common characteristics of the abstract, such as the dog can be defined as a class, the dog has a name will call will jump; A class is a special function. Just as a function definition has a declaration and an expression, a class definition has a declaration and an expression. However, unlike a function declaration, a class declaration cannot be promoted. Classes also have a name attribute

    / / the class declaration
    class Dog {
        constructor(name) {
            this.name = name
        }
        bark() {}
        jump() {}
    }
    Dog.name  // 'Dog'
    
    // Class expression: can be named (the class name attribute takes the class name) or not named (the class name attribute takes the variable name)
    let Animal2 = class {
        // xxx
    }
    Animal2.name  // 'Animal2'
    Copy the code
  • Classes in JS are based on prototypes (using functions to simulate classes, which are the syntactic sugar of constructors), similar to constructors in ES5, but there are some differences, such as the internal methods of a class that cannot be iterated:

    class Dog {
        constructor() {}
        bark() {}
        jump(){}}Object.keys(Dog.prototype)  / / []
    
    / / similar to
    function Dog2(){
    }
    Dog2.prototype = {
        constructor() {},
        bark() {},
        jump(){},}Object.keys(Dog2.prototype)  // ['constructor', 'bark', 'jump']
    Copy the code
  • Add a new method to the class based on the prototype:

    Object.assign(Dog.prototype, {
        wag() {}  / / wag its tail
    })
    Copy the code
  • Both the class declaration and the body of the class expression are executed in strict mode. For example, constructors, static methods, prototype methods, getters, and setters are all executed in strict mode.

  • This inside a class defaults to an instance of the class, so calling a prototype method or static method directly causes this to point to the runtime environment, whereas inside a class is in strict mode, so this is undefined:

    class Dog {
        constructor(name) {
            this.name = name
        }
        bark() {
            console.log( `The ${this.name} is bark.`)}static jump() {
            console.log( `The ${this.name} is jump.`)}}let dog = new Dog('rhubarb')
    let { bark } = dog
    let { jump } = Dog
    bark()  // TypeError: Cannot read property 'name' of undefined
    jump()  // TypeError: Cannot read property 'name' of undefined
    Copy the code

Methods and keywords:

  • The constructor method is the default method of the class and will be called automatically when an instance is generated using the new keyword. A class must have a constructor method, an empty one will be added automatically if no definition is shown; By default, constructor returns an instance object:

    class Point {}
    
    / / is equivalent to
    class Point {
        constructor(){}}Copy the code
  • Intercepting read/write operations on a property with the GET and set keywords:

    class Dog {
        get age() {return 1
        }
        set age(val) {this.age = val
        }
    }
    Copy the code
  • Static methods do not exist on the class stereotype, so they cannot be called by the class instance. They can only be called by the class name. Static methods and stereotype methods can have the same name:

    class Dog {
        bark() {}
        jump() {
            console.log('Prototype method')}static jump() {
            console.log('Static methods')}}Object.getOwnPropertyNames(Dog.prototype)  // ['constructor', 'bark', 'jump']
    Dog.jump()  // 'static method'
    let dog = new Dog()
    dog.jump()  // 'prototype method'
    Copy the code
  • Public and private fields:

    Static public fields, like static methods, can only be called by the class name; Private properties and private methods can only be called inside the class. External calls will report an error:

    class Dog {
        age = 12                   // Public field
        static sex = 'male'        // Static public fields
        #secret = 'I am a good friend to mankind'  // Private field
        #getSecret() {              // Private method
            return this.#secret
        }
    }
    Dog.sex  // 'male'
    let dog = new Dog()
    dog.#getSecret()  // SyntaxError
    Copy the code

    Public and private field declarations are an experimental feature proposed by the JavaScript Standards Committee TC39 (Phase 3). Support in browsers is limited, but you can use this functionality after building through systems such as Babel.

  • The new.target property allows you to check whether a function, constructor, or class was called using the new operator. In a function or constructor that is initialized with the new operator, new.target returns a reference to the constructor or function. In a normal function call, new.target is undefined, and a subclass that inherits from its parent class is returned:

    class Dog {
        constructor() {
            console.log(new.target.name)
        }
    }
    function fn(){
        if (!new.target) return 'new target is undefined'
        console.log('fn is called by new')}let dog = new Dog()  // 'Dog'
    fn()                 // 'new target is undefined'
    new fn()             // 'fn is called by new'
    Copy the code

Class inheritance:

  • Classes can be inherited through the extends keyword. If a subclass displays a definition for constructor, then the super() method must be called internally with the internal this pointing to the current subclass:

    class Animal {
        constructor(name) {
            this.name = name
        }
        run() {
            console.log(`The ${this.name} is running.`)}}class Dog extends Animal{
        constructor(name){
            super(name)  // Must be called
            this.name = name
        }
        bark() {
            console.log(`The ${this.name} is barking.`)}}let dog = new Dog('rhubarb')
    dog.run()  // 'Rhubarb is running.'
    Copy the code
  • Calling the constructor of the superclass through super() or calling the prototype method of the superclass through super; It is also possible to call a static method of a superclass in a static method of a subclass using super:

    // Based on the code above
    class Dog extends Animal{
        constructor(name){
            super(name)  // Call the superclass constructor
            this.name = name
        }
        bark() {
            super.run()  // Call the superclass prototype method
            console.log(`The ${this.name} is barking.`)}}let dog = new Dog()
    dog.bark()s
    // 'Rhubarb is running.'
    // 'Rhubarb is barking.'
    Copy the code
  • The __proto__ attribute of a subclass, which represents inheritance from the constructor, always points to the superclass; The __proto__ property of a subclass’s prototype property, which represents the inheritance of the method, always points to the parent class’s prototype property:

    class Animal {}
    class Dog extends Animal {}
    
    Dog.__proto__ === Animal  // true
    Dog.prototype.__proto__ === Animal.prototype  // true
    Copy the code

    The stereotype of the subclass stereotype points to the stereotype of the superclass:

    // Based on the above code
    let animal = new Animal()
    let dog = new Dog()
    dog.__proto__.__proto__  === animal.__proto__  // true
    Copy the code
  • Extends also extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends native constructors.

    • String()
    • Number()
    • Boolean()
    • Array()
    • Object()
    • Function()
    • Date()
    • RegExp()
    • Error()
    class MyString extends String {
        constructor(name){
            super(name)
            this.name = name
        }
        welcome() {
            return `hello The ${this.name}`}}let ms = new MyString('布兰')
    ms.welcome()      // 'Hello Bran'
    ms.length         / / 2
    ms.indexOf('blue')  / / 1
    Copy the code

Module

  • Traditional browser loading module:

    // Load synchronously
    <script src="test.js"></script>
    
    // defer load asynchronously: Execute in sequence, and execute after the document is parsed;
    <script src="test.js" defer></script>
    
    // async: load asynchronously.
    <script src="test.js" async></script>
    Copy the code
  • The browser can now load the script as module (plus type=”module”), and by default it will load asynchronously as defer; Module loading in ES6 relies on the import and export commands; Strict mode is automatically adopted inside the module:

    // The module is loaded
    <script type="module" src="test.js"></script>
    Copy the code
  • Export is used to output the external interface of a module. Only one export default can exist in a module. The following are the methods of output module interfaces:

    // person.js
    // Write method 1: separate export
    export const name = '布兰'
    export const age = 12
    
    // Write method 2: export on demand
    const name = '布兰', age = 12
    export { name, age }
    
    // Write method 3: rename and export
    const name = '布兰', age = 12
    export { name as name1, age as age1 }
    
    // Method 4: export by default
    const name = '布兰'
    export default name
    Copy the code
  • The import interface is used to input other modules:

    // Import on demand
    import { name, age } './person.js'
    
    // Rename after import
    import { name1 as name, age1 as age } from './person.js'
    
    // Import by default
    import person from './person.js'
    
    // Import the whole
    import * as person from './person.js'
    
    // Mixed import
    import _, { each } from 'lodash'
    Copy the code

    Import details of the import:

    • The name of the imported variable must be the same as the name of the exported moduleasRename;
    • Imported variables are read-only and cannot be overwritten.
    • importThe command has the effect of promotion. It will be promoted to the head of the whole module and executed first.
    • importIs a compile-time import, so it cannot be written to a code block (e.gifInside the judgment block) or inside the function;
    • importThe code of the loaded module is executed, or only once if the same module is imported repeatedly.
  • Compound writing of import and export: export and import statements can be combined to form a single line, thus directly forwarding the interface of an external module to the current module. Compound writing also supports renaming with as. The following example requires forwarding the person.js interface in the hub.js module:

    // person.js
    const name = '布兰', age = 12
    export { name, age }
    // On-demand forwarding interface (transfer module: hub.js)
    export { name, age } from './person.js'
    / / equivalent to
    import { name, age } from './person.js'
    export { name, age }
    
    // person.js
    const name = '布兰', age = 12
    export default { name, age }
    // Forward the default interface (forwarding module: hub.js)
    export { default } from './person.js'
    / / equivalent to
    import person from './person.js'
    export default person
    Copy the code
  • Differences between ES6 and CommonJS modules

    • CommonJSThe module outputs a copy of a value (once it outputs a value, it is not affected by changes within the module),ES6The module outputs a reference to the value (it is a dynamic reference and does not cache the value, the variables in the module are bound to the module in which they are located, and when the script is actually executed, the value is valued in the loaded module based on the read-only reference).
    • CommonJSModules are loaded at runtime,ES6Modules are compile-time output interfaces;
    • CommonJSThe modulerequire()Is to load modules synchronously,ES6The moduleimportCommands are loaded asynchronously, with a separate module dependent parsing phase;

The Iterator and for… of

  • Iterator Provides a mechanism for accessing various data structures in a uniform order. Typically deployed within or on a prototype of an iterable data structure. For an object to be an iterator, it must have a next() method, which returns an object each time it is executed. This object contains a done property (which is a Boolean; true means to continue the next iteration) and a value property (the value of each iteration) :

    // Generate an iterator
    let makeIterator = (arr) = > {
        let index = 0
        return {
            next(){
                return index < arr.length ? {
                    value: arr[index++],
                    done: false}, {done: true}}}}Copy the code
  • Iterable Data structures: There must be a Symbol. Iterator property inside or on the prototype (or Symbol. AsyncIterator if asynchronous). This property is a function that produces an iterator when executed:

    let obj = {
        [Symbol.iterator]() {
            return {
                index: 0.next() {
                    if (this.index < 3) {
                        return {value: this.index++, done: false}}else {
                        return {done: true}
                    }
                } 
            }
        }
    }
    for (let x of obj) {
        console.log(x)  / / 0 1 2
    }
    Copy the code

    Some of the built-in iterable data structures are String, Array, TypedArray, Map and Set, Arguments, and NodeList:

    let si = 'hi'[Symbol.iterator]()
    si         // StringIterator
    si.next()  // {value: 'h', done: false}
    si.next()  // {value: 'i', done: false}
    si.next()  // {value: undefined, done: true}
    Copy the code
  • for… Of: For traversing iterable data structures:

    • Traversal string:for... inGet index,for... ofGet the value;
    • Traversing the number group:for... inGet index,for... ofGet the value;
    • Iterating over objects:for... inAccess to key,for... ofDeploy it on your own[Symbol.iterator]Interface;
    • traverseSet:for... ofGet the value,for (const v of set);
    • traverseMap:for... ofGet the key-value pair,for (const [k, v] of map);
    • Iterating over an array of classes: containslengthThe object,argumentsObjects,NodeListObject (noIteratorAn array of classes for the interface is availableArray.from()Conversion);
    // Iterate over the string
    for (let x of 'abc') {
        console.log(x)  // 'a' 'b' 'c'
    }
    
    // Iterate over the array
    for (let x of ['a'.'b'.'c']) {
        console.log(x)  // 'a' 'b' 'c'
    }
    
    / / traverse the Set
    let set = new Set(['a'.'b'.'c'])
    for (let x of set) {
        console.log(x)  // 'a' 'b' 'c'
    }
    
    / / traverse Map
    let map = new Map([['name'.'布兰'], ['age'.12]])
    for (let [key, value] of map) {
        console.log(key + ':' + value)  // 'name: Bran ' 'age: 12'
    }
    Copy the code
  • for… Of and the for… In contrast to

    Common: The ability to break, continue, and return out of a loop; Differences: – for… Characteristics of in: can only iterate over the key, will iterate over the prototype properties, traversal is not in order, suitable for the object traversal; – for… Features of of: the ability to iterate over values (some data structures can iterate over keys and values, such as Map), not over the key values on the prototype, the order of the iterate is the order of data addition, suitable for iterable data structures;

Promise

This knowledge can be directly read in my previous article: Understanding Promise is very complete.

Generator

  • Function * defines a Generator function. Calling a Generator function does not immediately execute, but returns a Generator object that conforms to the iterable protocol and the iterator protocol. In other words, the Generator can be iterated over.

  • The generator function internally uses yield to control pauses, and next() will resume execution. Its logic is as follows:

    • encounteryieldExpression is suspended to perform subsequent operations and will be followed byyieldThe value of the latter expression is the value of the returned objectvalueThe attribute value;
    • Next callnextMethod, the execution continues until the next one is encounteredyieldThe expression;
    • If you don’t meet a newyieldExpression, it runs until the end of the function, untilreturnStatement, and willreturnThe value of the expression following the statement, as the value of the returned objectvalueThe attribute value;
    • If the function doesn’treturnStatement, returns the object’svalueAttribute values forundefined;
    function* gen() {
        yield 'hello'
        yield 'world'
        return 'end'
    }
    let g = gen()
    g.next()  // {value: 'hello', done: false}
    g.next()  // {value: 'world', done: false}
    g.next()  // {value: 'end', done: true}
    Copy the code
  • You can use the yield* expression inside a Generator function to delegate to another Generator or to iterable objects such as arrays, strings, and so on. The value of the expression itself is what is returned when the iterator is closed (that is, when done is true) :

    function* inner() {
        yield* [1.2]
        return 'foo'
    }
    function* gen() {
        let result = yield* inner()
        console.log(result)
    }
    let g = gen()
    g.next()
    g.next()
    g.next()
    Copy the code

Example method:

  • Generator.prototype.next() : returns an object containing the properties done and value. The method can also pass a value to the generator by accepting an argument. If an argument is passed, it is passed to the variable to the left of the last yield statement:

    function* f() {
        let a = yield 12
        console.log(a)
        let b = yield a
        console.log(b)
    }
    let g = f()
    console.log(g.next('a'))
    console.log(g.next('b'))
    console.log(g.next('c'))
    // {value: 12, done: false}
    // 'b'
    // {value: 'b', done: false}
    // 'c'
    // {value: undefined, done: true}
    Copy the code
  • Generator. Prototype. Throw () : used to Generator throwing an exception, if internal captured will resume the execution of the Generator (that is, the execution of a yield expression), and returns with the done and the value of the two attributes of objects:

    function* gen() {
        try {
            yield 'a'
        } catch(e) {
            console.log(e)
        }
        yiele 'b'
        yield 'c'
    }
    let g = gen()
    g.next()
    g.throw('error a')
    g.next()
    // {value: "a", done: false}
    // 'error a' 
    // {value: "b", done: false}
    // {value: "c", done: false}
    Copy the code

    If the exception is not caught internally, the execution of the inner code will be interrupted (similar to an exception thrown by a throw, if not caught, the subsequent code will not be executed). In this case, the exception will be thrown externally. If next() is executed again, it returns an object with the done property true:

    function* gen() {
        yield 'a'
        yield 'b'
        yield 'c'
    }
    let g = gen()
    g.next()
    try {
        g.throw('error a')}catch(e) {
        console.log(e)
    }
    g.next()
    // {value: "a", done: false}
    // 'error a'
    // {value: undefined, done: true}
    Copy the code
  • The Generator. The prototype. The return () : returns the given value and end the Generator:

    function* gen() {
        yield 1
        yield 2
        yield 3
    }  
    let g = gen()
    g.next()         // { value: 1, done: false }
    g.return('foo')  // { value: "foo", done: true }
    g.next()         // { value: undefined, done: true }
    Copy the code

Application:

  • To synchronize an asynchronous operation, for example, to have multiple requests at the same time. The requests are sequential and can only be requested after the previous request has completed:

    function* main() {
        let res1 = yield request('a')
        console.log(res1)
        let res2 = yield request('b')
        console.log(res2)
        let res3 = yield request('c')
        console.log(res3)
    }
    function request(url) {
        setTimeout(function(){  // Simulate asynchronous requests
            it.next(url)
        }, 300)}let it = main()
    it.next()
    // 'a' 'b' 'c'
    Copy the code
  • Deploy the Iterator interface to an object:

    function* iterEntries(obj) {
        let keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
            let key = keys[i]
            yield [key, obj[key]]
        }
    }
    let obj = { foo: 3.bar: 7 }
    for (let [key, value] of iterEntries(myObj)) {
        console.log(key, value)
    }
    // 'foo' 3
    // 'bar' 7
    Copy the code

Refer to the article

  • Introduction to ECMAScript6
  • All features of ES6 (updated ES2020)
  • Nearly 10,000 words ES6 grammar knowledge added
  • Understanding Promise
  • for-await… of
  • Iteration_protocols
  • Object.fromEntries
  • WeakRef
  • Memory management
  • jshistory-cn
  • sameValueZero