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.
-
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.
-
There is also the problem of variable promotion
console.log(i)
var i = 1;
console.log(letI)
let letI = 2;
Copy the code
- Const is simple and cannot be modified based on let.
Arrow function
- 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
- Short arrow function
const arrowFn = (value) = > Number(value);
console.log(arrowFn('aaa'))
Copy the code
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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.
- 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
- 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
- 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
- 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
- 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
- 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
- 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.”