This is the 16th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

TIP 👉 carefully planted flowers do not hair, leisurely inserted liuliu chengyin. Yuan · Guan Hanqing, Lu Zhailang was beheaded by a wise man

preface

Brief introduction to ECMAScript specification development

Everyone has heard of ES6, ES7, ES2015, ES2016, ES2017… And so on. Some people describe it by version number, such as ES6 and ES7. Some people describe it by year, such as ES2015 and ES2016.

So what is the relationship between them? Let’s start from the development of JS.

history

ECMAScript is a standardized specification for a scripting language developed by Brandon Ek of Netscape; Originally named Mocha, it was later changed to LiveScript and finally renamed JavaScript[1]. In December 1995, Sun and Netscape jointly released JavaScript[2]. In November 1996, Netscape submitted JavaScript to the European Computer Manufacturers Association for standardization. The first version of ECMA-262 was adopted by the ECMA organization in June 1997. ECMAScript is the name of the scripting language standardized by ECMA-262.

Although JavaScript and JScript are ECMAScript compatible, they contain capabilities beyond ECMAScript [3].

From wikipedia.

In summary: Strictly speaking, ES6 refers to the ES2015 standards released in June 2015, but many people refer to ES2016, ES2017 and other standards when talking about ES6. So strictly speaking, it’s better to use years when talking about the ECMAScript standard. But it doesn’t matter. It doesn’t make much sense.

ESNext

ES6, 7, ES2015, why ESNext? What is ESNext?

In fact, ESNext is a generic reference, always pointing to the next version. For example, if the current latest version is ES2020, then ESNext refers to the standard that will be released in June 2021.

Common API parsing added in ES6 and later

Let and const

Let’s start with a classic interview question

for(var i=0; i<=3; i++){setTimeout(function() {  
        console.log(i)  
    }, 10);
} 
Copy the code

What will each output? Why is that? How can I modify it to print 0,1,2,3?

for(var i = 0; i <=3; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(i);
        }, 10);
    })(i);
}

for(let i=0; i<=3; i++){setTimeout(function() {  
        console.log(i)  
    }, 10);
} 
Copy the code

SetTimeout is asynchronous. In the next round of the event loop, wait until it is executed to find a reference to the I variable. So the function finds the I after it’s gone through, and now it’s going to be 4.

  1. While let introduces the concept of block-level scope. When setTimeout function is created, variable I is in scope. For each iteration of the loop, the referenced I is a different instance of I.

  2. There is also the problem of variable promotion

console.log(i)
var i = 1;

console.log(letI)
let letI = 2;
Copy the code
  1. Const is simple and cannot be modified based on let.

Arrow function

  1. The biggest difference: the arrow function “this” is defined while the normal function “this” is used.
const teacher = {
    name: 'lubai'.getName: function() {
        return `The ${this.name}`}}console.log(teacher.getName());

const teacher = {
    name: 'lubai'.getName: () = > {
        return `The ${this.name}`}}console.log(teacher.getName());
Copy the code
  1. Short arrow function
const arrowFn = (value) = > Number(value);

console.log(arrowFn('aaa'))
Copy the code
  1. Note that the arrow function cannot be used as a constructor

What does the constructor do? Change this to refer to the object from the new instance. What does the arrow function do? The this reference is determined at the time of the definition.

class

class Test {
    _name = ' ';
    constructor() {
        this.name = 'lubai';
    }

    static getFormatName() {
        return `The ${this.name} - xixi`;
    }

    get name() {
        return this._name;
    }

    set name(val) {
        console.log('name setter');
        this._name = val; }}console.log(new Test().name)
console.log(Test.getFormatName())
Copy the code

Template string

const b = 'lubai'
const a  = `${b} - xxxx`;
const c = 'I'm a new line I'm a new line! I'm changing lines again! `;
Copy the code

Write the render function to implement template render function.

const year = '2021'; 
const month = '10'; 
const day = '01'; 

let template = '${year}-${month}-${day}';
let context = { year, month, day };

const str = render(template)({year,month,day}); 

console.log(str) / / 2021-10-01

function render(template) {
    return function(context) {
        return template.replace(/ \ $\ {(. *?) \}/g.(match, key) = >context[key]); }}Copy the code

deconstruction

  1. Array deconstruction
// Base type destruct
let [a, b, c] = [1.2.3]
console.log(a, b, c) / / 1, 2, 3

// Object array destruct
let [a, b, c] = [{name: '1'}, {name: '2'}, {name: '3'}]
console.log(a, b, c) // {name: '1'}, {name: '2'}, {name: '3'}

/ /... deconstruction
let [head, ...tail] = [1.2.3.4]
console.log(head, tail) // 1, [2, 3, 4]

// Nested destruct
let [a, [b], d] = [1[2.3].4]
console.log(a, b, d) / / 1, 2, 4

// Destruct failed as undefined
let [a, b, c] = [1]
console.log(a, b, c) // 1, undefined, undefined

// Destruct the default assignment
let [a = 1, b = 2] = [3]
console.log(a, b) / / 3 and 2
Copy the code
  1. Object structure
// Object attribute destruct
let { f1, f2 } = { f1: 'test1'.f2: 'test2' }
console.log(f1, f2) // test1, test2

This is one of the differences between array deconstruction and object deconstruction
let { f2, f1 } = { f1: 'test1'.f2: 'test2' }
console.log(f1, f2) // test1, test2

// Deconstruct the object rename
let { f1: rename, f2 } = { f1: 'test1'.f2: 'test2' }
console.log(rename, f2) // test1, test2

// Nested destruct
let { f1: {f11}} = { f1: { f11: 'test11'.f12: 'test12'}}console.log(f11) // test11

/ / the default value
let { f1 = 'test1'.f2: rename = 'test2' } = { f1: 'current1'.f2: 'current2'}
console.log(f1, rename) // current1, current2
Copy the code
  1. What is the principle of deconstruction?

The Iterator interface for iterable objects, which is assigned by the Iterator to obtain the corresponding values sequentially.

3.1 So what is Iterator?

Iterator is an interface that provides a unified access mechanism for various types of data deconstruction. Any data deconstruction with an Iterator interface can process all the members of the data structure sequentially through iterating operations. The syntax for of in ES6 is equivalent to an Iterator, which automatically looks for the Iterator interface while iterating through data structures.

3.2 What is Iterator used for?

  • Provides a unified access interface for various data deconstructions
  • Enables data deconstruction to be processed in order
  • You can use the latest ES6 command for of to traverse
function generateIterator(array) {
    let nextIndex = 0
    return {
        next: () = > nextIndex < array.length ? {
            value: array[nextIndex++],
            done: false}, {value: undefined.done: true}}; }const iterator = generateIterator([0.1.2])

console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
Copy the code

3.3 What is an iterable?

An iterable is an implementation of the Iterator interface. This is a complement to ECMAScript 2015, it is not a built-in or syntax, but only a protocol. Any object that follows this protocol can be an iterable. An iterable has two protocols: the iterable protocol and the iterator protocol.

  • Iterable protocol: Objects must implement iterator methods. That is, an object or its prototype chain must have a property called symbol. iterator. The value of this property is a no-parameter function that returns the iterator protocol.

  • Iterator protocol: defines a standard way to produce a finite or infinite sequence of values. It requires that you implement a next() method that returns an object with done(Boolean) and value attributes.

3.4 We can implement a for of traversal object?

A custom data structure that has an Iterator interface and deploys it to its Symbol. Iterator property can become an iterable that can be iterated over by a for of loop.

const obj = {
    count: 0[Symbol.iterator]: () = > {
        return {
            next: () = > {
                obj.count++;
                if (obj.count <= 10) {
                    return {
                        value: obj.count,
                        done: false}}else {
                    return {
                        value: undefined.done: true
                    }
                }
            }
        }
    }
}

for (const item of obj) {
    console.log(item)
}
 
Copy the code

or

const iterable = {
    0: 'a'.1: 'b'.2: 'c'.length: 3[Symbol.iterator]: Array.prototype[Symbol.iterator],
};

for (const item of iterable) {
    console.log(item);
}
Copy the code

traverse

  1. for in

When iterating over a number array, key is the array subscript string. Traversal object, key is the object field name.

let obj = {a: 'test1'.b: 'test2'}
for (let key in obj) {
    console.log(key, obj[key])
}
Copy the code

Disadvantages:

  • For in not only iterates through the current object, but also includes enumerable properties on the prototype chain
  • For in is not suitable for traversing a number group and is mainly used as an object
  1. for of

Create an iteration loop on an iterable (including Array, Map, Set, String, TypedArray, Arguments, NodeList), call a custom iteration hook, and execute a statement for the value of each different property.

let arr = [{age: 1}, {age: 5}, {age: 100}, {age: 34}]
for(let {age} of arr) {
    if (age > 10) {
        break // for of allow interrupt
    }
    console.log(age)
}
Copy the code

Advantages:

  • For of only iterates over the current object

Object

  1. Object.keys

This method returns an array of the given object’s own enumerable properties.

const obj = { a: 1.b: 2 };
const keys = Object.keys(obj); // [a, b]
Copy the code

Write a function to simulate object.keys?

function getObjectKeys(obj) {
    const result = [];
    for (const prop in obj) {
        if(obj.hasOwnProperty(prop)) { result.push(prop); }}return result;
}

console.log(getObjectKeys({
    a: 1.b: 2
}))
Copy the code
  1. Object.values

This method returns an array of all enumerable property values for a given object itself.

const obj = { a: 1.b: 2 };
const keys = Object.keys(obj); / / [1, 2]
Copy the code

Write a function to simulate object.values?

function getObjectValues(obj) {
    const result = [];
    for (const prop in obj) {
        if(obj.hasOwnProperty(prop)) { result.push(obj[prop]); }}return result;
}

console.log(getObjectValues({
    a: 1.b: 2
}))
Copy the code
  1. Object.entries

This method returns an array of key-value pairs of the given object’s own enumerable properties.

const obj = { a: 1.b: 2 };
const keys = Object.entries(obj); // [ [ 'a', 1 ], [ 'b', 2 ] ]
Copy the code

Write a function to simulate Object.entries?

function getObjectEntries(obj) {
    const result = [];
    for (const prop in obj) {
        if(obj.hasOwnProperty(prop)) { result.push([prop, obj[prop]]); }}return result;
}

console.log(getObjectEntries({
    a: 1.b: 2
}))
Copy the code
  1. Object.getOwnPropertyNames

This method returns an array of enumerable or non-enumerable property names that obJ owns.

What does this code output?

Object.prototype.aa = '1111';

const testData = {
    a: 1.b: 2
}

for (const key in testData) {
    console.log(key);
}

console.log(Object.getOwnPropertyNames(testData))
Copy the code
  1. Object.getOwnPropertyDescriptor

What is a descriptor? The property descriptor of the object is an object. Contains the following attributes:

  • Configurable. If false, any attempt to delete the target property or modify the property property (writable, 64x, Enumerable) will be invalidated. Therefore, if each property has its own features, you can set the signals to true.
  • Writable Indicates whether it is writable. If set to false, any overrides to this property are invalid (but will not report errors, in strict mode). The default is false.
  • Enumerable. Can be iterated through in a for-in loop or enumerated in object.keys.
const object1 = {};
Object.defineProperty(object1, 'p1', {
  value: 'lubai'.writable: false
});

object1.p1 = 'not lubai';

console.log(object1.p1);
Copy the code

When it comes to defineProperty, you can’t do without a Proxy.

// const obj = {};
// let val = undefined;
// Object.defineProperty(obj, 'a', {
// set: function (value) {
// console.log(`${value} - xxxx`);
// val = value;
/ /},
// get: function () {
// return val;
/ /},
// configurable: true,
// })

// obj.a = 111;

// console.log(obj.a)

const obj = new Proxy({}, {
    get: function (target, propKey, receiver) {
        console.log(`getting ${propKey}`);
        return target[propKey];
    },
    set: function (target, propKey, value, receiver) {
        console.log(`setting ${propKey}`);
        return Reflect.set(target, propKey, value, receiver); }}); obj.something =1;
console.log(obj.something);
Copy the code

And what is Reflect?

  • Put some methods of Object that are clearly internal to the language (such as Object.defineProperty) on Reflect. At this stage, some methods are deployed on both Object and Reflect objects, and future new methods will only be deployed on Reflect objects. That is, from the Reflect object you can get the methods inside the language
  • Make all Object operations functions. Some Object operations are imperative, such as name in obj and delete obj[name], while reflect.has (obj, name) and reflect.deleteProperty (obj, name) make them functional behavior.
  • The Reflect method corresponds to the Proxy method. As long as it is a Proxy method, the corresponding method can be found on the Reflect object. This allows the Proxy object to easily call the corresponding Reflect method, completing the default behavior as a basis for modifying the behavior. That is, no matter how the Proxy changes the default behavior, you can always get the default behavior in Reflect.

Note, however, that an object whose writable is set to false via defineProperty cannot use Proxy

const target = Object.defineProperties({}, {
    foo: {
        value: 123.writable: false.configurable: false}});const proxy = new Proxy(target, {
    get(target, propKey) {
        return 'abc'; }}); proxy.fooCopy the code
  1. Object.create()

The object.create () method creates a new Object and takes the first argument of the method as the value of the __proto__ property of the new Object (the new Object is created from an existing Object as a prototype). The object.create () method takes a second optional argument, which is an Object. Each property of the Object is treated as a property of the new Object. Object attribute value to descriptor (Object. GetOwnPropertyDescriptor (obj, ‘key’)) in the form of and enumerable to false by default

const person = {
    isHuman: false.printIntroduction: function () {
        console.log(`My name is The ${this.name}. Am I human? The ${this.isHuman}`); }};const me = Object.create(person);

me.name = "lubai";
me.isHuman = true;
me.printIntroduction();

console.log(person);

const myObject = Object.create(null)
Copy the code

How does passing in the second argument work?

function Person(name, sex) {
    this.name = name;
    this.sex = sex;
}

const b = Object.create(Person.prototype, {
    name: {
        value: 'coco'.writable: true.configurable: true.enumerable: true,},sex: {
        enumerable: true.get: function () {
            return 'hello sex'
        },
        set: function (val) {
            console.log('set value:' + val)
        }
    }
})

console.log(b.name)
console.log(b.sex)
Copy the code

So what does object.create (null) mean? Create ({}) or declare {}.

Object.create(null) Creates an Object whose prototype chain is null, that is, Fn. Prototype = null

const b = Object.create(null) // Returns a pure {} object, no prototype

b / / {}
b.__proto__ // undefined
b.toString() // throw error
Copy the code

So if you want to create a very clean Object without any properties on the prototype chain, then you don’t need to consider the properties of the prototype chain when iterating with Object.create(null). For in.

  1. Object.assign

Shallow copy, similar to {… a, … b };

function shallowClone(source) {
    const target = {};
    for (const i in source) {
        if(source.hasOwnProperty(i)) { target[i] = source[i]; }}return target;
}

const a = {
    b: 1.c: {
        d: 111}}const b = shallowClone(a);


b.b = 2222;

b.c.d = 333;

console.log(b)
console.log(a)
Copy the code
  1. Object.is
const a = {
    name: 1
};
const b = a;
console.log(Object.is(a, b))

console.log(Object.is({}, {}))
Copy the code

Promise

I’ve covered most of promise, so let’s review promise.all

function PromiseAll(promiseArray) {
    return new Promise(function (resolve, reject) {
        // Determine the parameter type
        if (!Array.isArray(promiseArray)) {
            return reject(new TypeError('arguments muse be an array'))}let counter = 0;
        let promiseNum = promiseArray.length;
        let resolvedArray = [];
        for (let i = 0; i < promiseNum; i++) {
            // 3. Here we use promise.resolve.
            Promise.resolve(promiseArray[i]).then((value) = > {
                counter++;
                resolvedArray[i] = value; // 2. Push instead of index assignment, ok
                if (counter == promiseNum) { ResolvedArr. Length === promiseNum
                    // 4. If you are not in the "then", you are in the "then".
                    resolve(resolvedArray)
                }
            }).catch(e= >reject(e)); }})}/ / test
const pro1 = new Promise((res, rej) = > {
    setTimeout(() = > {
        res('1')},1000)})const pro2 = new Promise((res, rej) = > {
    setTimeout(() = > {
        res('2')},2000)})const pro3 = new Promise((res, rej) = > {
    setTimeout(() = > {
        res('3')},3000)})const proAll = PromiseAll([pro1, pro2, pro3])
    .then(res= >
        console.log(res) // Print after 3 seconds ["1", "2", "3"]
    )
    .catch((e) = > {
        console.log(e)
    })
Copy the code

Let’s write a promise.allseettled that needs to return the status and results of all promises

function PromiseAllSettled(promiseArray) {
    return new Promise(function (resolve, reject) {
        // Determine the parameter type
        if (!Array.isArray(promiseArray)) {
            return reject(new TypeError('arguments muse be an array'))}let counter = 0;
        const promiseNum = promiseArray.length;
        const resolvedArray = [];
        for (let i = 0; i < promiseNum; i++) {
            Promise.resolve(promiseArray[i])
                .then((value) = > {
                    resolvedArray[i] = {
                        status: 'fulfilled',
                        value
                    };

                })
                .catch(reason= > {
                    resolvedArray[i] = {
                        status: 'rejected',
                        reason
                    };
                })
                .finally(() = > {
                    counter++;
                    if (counter == promiseNum) {
                        resolve(resolvedArray)
                    }
                })
        }
    })
}

Copy the code

An array of

  1. Array.flat

The Flat () method recurses through the array of numbers at a specified depth and returns all the elements in a new array combined with the elements in the traversed subarray

const arr1 = [1.2[3.4]];
arr1.flat();
// [1, 2, 3, 4]

const arr2 = [1.2[3.4[5.6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

const arr3 = [1.2[3.4[5.6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

// Use Infinity to expand nested arrays of any depth
const arr4 = [1.2[3.4[5.6[7.8[9.10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Copy the code

How to simulate array.flat?

// Use reduce, concat, and recursion to expand an infinite number of nested arrays
const arr1 = [1.2.3[1.2.3.4[2.3.4]]];

function flatDeep(arr, d = 1) {
    if (d > 0) {
        return arr.reduce((res, val) = > {
            if (Array.isArray(val)) {
                res = res.concat(flatDeep(val, d - 1))}else {
                res = res.concat(val);
            }
            returnres; }}, [])else {
        return arr.slice()
    }
};

console.log(flatDeep(arr1, Infinity))
// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
Copy the code

Regardless of depth, let’s just give him an infinite draw

function flatten(arr) {
    let res = [];
    let length = arr.length;
    for (let i = 0; i < length; i++) {
        if (Object.prototype.toString.call(arr[i]) === '[object Array]') {
            res = res.concat(flatten(arr[i]))
        } else {
            res.push(arr[i])
        }
    }
    return res
}

// If the array elements are of type Number
function flatten(arr) {
	return arr.toString().split(', ').map(item= > +item)
}

function flatten(arr){
    while(arr.some(item= >Array.isArray(item))){ arr = [].concat(... arr); }return arr;
}
Copy the code
  1. Array.includes

The includes() method is used to determine whether an array contains a specified value, returning true if it does and false otherwise.

const array1 = [1.2.3];

console.log(array1.includes(2));

const pets = ['cat'.'dog'.'bat'];

console.log(pets.includes('cat'));
Copy the code

It actually takes two arguments, but we usually only use one.

  • valueToFind

The value of the element to look up.

  • FromIndex optional

Start looking for valueToFind at the fromIndex index. 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
[1.2.NaN].includes(NaN); // true

// fromIndex is greater than or equal to the array length
var arr = ['a'.'b'.'c'];

arr.includes('c'.3);   // false
arr.includes('c'.100); // false

// The calculated index is less than 0
var arr = ['a'.'b'.'c'];

arr.includes('a', -100); // true
arr.includes('b', -100); // true
arr.includes('c', -100); // true
Copy the code
  1. Array.find

The find() method returns the value of the first element in the array that satisfies the provided test function. Otherwise return undefined.

Callback The function that executes on each item of the array, taking three arguments:

  • element

The element currently traversed.

  • The index of the optional

The index currently traversed.

  • An array of optional

The array itself.

const test = [
    {name: 'lubai'.age: 11 },
    {name: 'xxx'.age: 100 },
    {name: 'nnn'.age: 50}];function findLubai(teacher) {
    return teacher.name === 'lubai';
}

console.log(test.find(findLubai));
Copy the code
  1. Array.from

The 4.1 array.from () method creates a new, shallow-copy Array instance from an array-like or iterable.

  • arrayLike

A pseudo-array object or iterable that you want to convert to an array.

  • MapFn optional

If specified, each element in the new array executes the callback function.

4.2 Array.from() You can create an Array object by:

  • Pseudo-array object (any object with a length attribute and index attributes)
  • Iterable (you can get elements of an object, such as maps and sets)
console.log(Array.from('foo'));

console.log(Array.from([1.2.3].x= > x + x));

const set = new Set(['foo'.'bar'.'baz'.'foo']);
Array.from(set);
// [ "foo", "bar", "baz" ]

const map = new Map([[1.2], [2.4], [4.8]]);
Array.from(map);
// [[1, 2], [2, 4], [4, 8]]

const mapper = new Map([['1'.'a'], ['2'.'b']]);
Array.from(mapper.values());
// ['a', 'b'];

Array.from(mapper.keys());
/ / (' 1 ', '2');
Copy the code

So what can we do about array de-duplication?

function unique (arr) {
  return Array.from(new Set(arr))
  // return [...new Set(arr)]
}
const test = [1.1.'true'.'true'.true.true.15.15.false.false.undefined.undefined.null.null.NaN.NaN.'NaN'.0.0.'a'.'a'];
console.log(unique(test));


function unique(arr) {
    const map = new Map(a);const array = []; // The array is used to return the result
    for (let i = 0; i < arr.length; i++) {
        if(! map.has(arr[i])) {// If there is a key value
            array.push(arr[i]);
            map.set(arr[i], true); }}return array;
}

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error! ')
        return
    }
    const array = [];
    for (let i = 0; i < arr.length; i++) {
        if(! array.includes(arr[i])) {//includes checks whether the array has a valuearray.push(arr[i]); }}return array
}
Copy the code
  1. Array.of

The array.of () method creates a new Array instance with a variable number of arguments, regardless of the number or type of arguments.

Array.of(7);       / / [7]
Array.of(1.2.3); / / [1, 2, 3]
Copy the code

So how do you simulate that?

Array.of = function() {
    return Array.prototype.slice.call(arguments);
};
Copy the code

async await yeild

“Feel free to discuss in the comments section.”

Hope to finish watching friends can give a thumbs-up, encourage once