promise

  • PromiseIs a class whose constructor needs to pass in an executorexecutor
  • executorTwo parameters:resolve reject
  • Create one by defaultpromiseThere are three states:pending fulfilled rejected
  • When the call succeeds or fails, a success cause or failure cause needs to be passed
  • If the state goes to zeroresolvedorrejectedThe status cannot be changed again
  • PromiseHas an instance ofthenMethod, which can be called multiple times, returns onePromiseobject
  • Throwing an exception is handled as a failure

Promise A + implementation

const status = {
    PENDING: 'PENDING'.FULFILLED: 'FULFILLED'.REJECTED: 'REJECTED'
}
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) { // Avoid circular references
        return reject(new TypeError('Error'));
    }
    // Determine the type of x. If x is an object or a function, x may be a promise, otherwise it cannot be a promise
    if(x ! = =null && (typeof x === 'object' || typeof x === 'function')) {
        let called = false; // There may be more than one promise implementation, but all follow the promise A + specification, we wrote the promise called, but in order to comply with the specification to add this control, because others write the promise may have multiple calls.
        try {
            // Then methods can be defined by getters, so it is risky to set then methods in a try... catch... In the
			// Someone else wrote promises like this
			// Object.defineProperty(promise, 'then', {
			// get() {
			// throw new Error();
			/ /}
			// })
            let then = x.then;
            if (typeof then === 'function') {
                // x.then(()=>{}, ()=>{}); Don't write it this way, in case you get an error, and it also prevents multiple values
				// let obj = {
				// a: 1,
				// get then() {
				// if (this.a++ == 2) {
				// throw new Error();
				/ /}
				// console.log(1);
				/ /}
				// }
				// obj.then;
				// obj.then
                then.call(x, y= > {
                    if (called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject); // The result of the current promise resolution may still be a promise, until it is resolved to a normal value
                }, e= > {
                    if (called) return;
                    called = true;
                    reject(e);
                });
            } else {
                resolve(x); // Plain object resolve directly}}catch (e) {
            if (called) return;
            called = true; reject(e); }}else {
        resolve(x); // The basic type is direct resolve}}class Promise {
    constructor(executor) {
        this.status = status.PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = []; // Save callback successfully
        this.onRejectedCallbacks = []; // Store failed callback
        const resolve = (value) = > {
            if (value instanceof Promise) {
                return value.then(resolve, reject); // If value is a Promise, execute recursively
            }
            if (this.status === status.PENDING) {
                this.status = status.FULFILLED;
                this.value = value;
                this.onResolvedCallbacks.forEach(fn= >fn()); }}const reject = (reason) = > {
            if (this.status === status.PENDING) {
                this.status = status.REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn= >fn()); }}try {
            executor(resolve, reject);
        } catch(e) { reject(e); }}then(onFulfilled, onRejected) {
        // ondepressing onRejected is optional
        Resolve (1).then().then((value) => console.log(value))
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : data= > data;
        onRejected = typeof onRejected === 'function' ? onRejected : e= > { throw e }; 
        let promise2 = new Promise((resolve, reject) = > { // then must return a new promise
            if (this.status === status.FULFILLED) {
                setTimeout(() = > { // This is a pity, onRejected asynchronously, and promise2 can be fulfilled
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) { reject(e); }},0)}if (this.status === status.REJECTED) {
                setTimeout(() = > {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) { reject(e); }},0)}if (this.status === status.PENDING) { // handle asynchrony
                this.onResolvedCallbacks.push(() = > {
                    setTimeout(() = > {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch(e) { reject(e); }},0)});this.onRejectedCallbacks.push(() = > {
                    setTimeout(() = > {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch(e) { reject(e); }},0)})}})returnpromise2; }}Copy the code

Promise related API implementation

all

static all(promises) {
    return new Promise((resolve, reject) = > {
        let res = [];
        let count = 0;
        const len = promises.length;
        for (let i = 0; i < len; i++) {
            const promise = promises[i];
            promise.then(value= > {
                res[i] = value;
                if (++count === len) {
                    resolve(res);
                }
            }, reject)
        }
    });
}
Copy the code

race

static race(promises) {
    return new Promise((resolve, reject) = > {
        promises.forEach(promise= > {
            promise.then(resolve, reject)
        })
    });
}
Copy the code

allSettled

static allSettled(promises) {
    return new Promise(resolve= > {
        let count = 0;
        let res = [];
        const callback = (value, index) = > {
            res[index] = value;
            if (++count === promises.length) {
                resolve(res);
            }
        }
        promises.forEach((promise, index) = > {
            promise.then(value= > {
                callback(value, index);
            }, reason= >{ callback(reason, index); })}); }); }Copy the code

resolve

static resolve(value) {
    return new Promise(resolve= > resolve(value));
}
Copy the code

reject

static reject(reason) {
    return new Promise((resolve, reject) = > reject(reason));
}
Copy the code

catch

catch (errCallback) { 
    return this.then(null, errCallback);
}
Copy the code

Event publishes the subscription implementation

  • Event is one of the dependent means of communication for front-end components and has three core methods
  1. On: Passes in the function to listen onfnAnd event triggeringkey
  2. Off: Incoming to be removedkeyfn
  3. Emit: Pass in the event that needs to be emittedkeyAnd arguments to call the listening function
class EventEmitter {
    constructor() {
        this._events = new Map(a); }on(eventName, fn) {
        let handler = this._events.get(eventName);
        if(! handler) {this._events.set(eventName, [fn])
        } else {
            handler.push(fn);
        }
        return this;
    }
    off(eventName, fn) {
        let handler = this._events.get(eventName);
        if (Array.isArray(handler)) {
            if (fn) {
                let index = handler.indexOf(fn);
                if(index ! = = -1) {
                    handler.splice(index, 1); }}else {
                handler.length = 0; }}return this;
    }
    emit(eventName, ... args) {
        let handler = this._events.get(eventName);
        if (Array.isArray(handler)) {
            handler.forEach(fn= > {
                fn(...args);
            })
        }
        return this; }}Copy the code

If the throttle

Function image stabilization

  • How it works: The callback is executed n seconds after the event is triggered. If it is triggered again within n seconds, the timer is reset
  • Scenario: Input box Input content query and download resource buttons
const debounce = (func, wait = 500, immediate = false) = > {
    let timer = null; 
    return function(. args) {
        if(! timer && immediate) { func.apply(this, args);
        }
        clearTimeout(timer);
        timer = setTimeout(() = > {
            timer = null;
            if(! immediate) { func.apply(this, args) } }, wait); }}Copy the code

Function of the throttle

  • Principle: Specifies that only one function can be triggered in a unit of time. If more than one function is fired in this unit of time, only one function will take effect
  • Scenario: Often used to listen for scrollbars
const throttle = (func, wait = 500) = > {
    let timer = null;
    let prevTime = 0;
    return function(. args) {
        let now = new Date(a);let remaining = wait - (now - prevTime);
        if (remaining <= 0) {
            // The interval exceeds the frequency
            timer = null;
            prevTime = now;
            func.apply(this, args);
        } else if(! timer) { timer =setTimeout(() = > {
                func.apply(this, args);
                clearTimeout(timer);
                timer = null;
                prevTime = new Date(a); }, remaining); }}}Copy the code

Deep copy

For more details, please click on my blog to learn more about shallow copy and deep copy, which has a very detailed explanation.

const arrayTag = '[object Array]'
const objectTag = '[object Object]'
const mapTag = '[object Map]'
const setTag = '[object Set]'
const functionTag = '[object Function]';
const boolTag = '[object Boolean]'
const dateTag = '[object Date]'
const errorTag = '[object Error]'
const numberTag = '[object Number]'
const regexpTag = '[object RegExp]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'

function cloneArray(array) {
    const { length } = array;
    const result = new array.constructor(length);
  
    if (length && typeof array[0= = ='string' && hasOwnProperty.call(array, 'index')) {
        result.index = array.index;
        result.input = array.input;
    }
    return result;
}

function cloneSymbol(symbol) {
    return Object(Symbol.prototype.valueOf.call(symbol));
}

function cloneRegExp(regexp) {
    const reFlags = /\w*$/;
    const result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
    result.lastIndex = regexp.lastIndex;
    return result;
}

function initCloneTargetByTag(target, tag) {
    const Ctor = target.constructor;
    switch (tag) {
        case boolTag:
        case dateTag:
            return new Ctor(+target);

        case numberTag:
        case stringTag:
        case errorTag:
            return new Ctor(target);

        case objectTag:
        case mapTag:
        case setTag:
            return new Ctor();

        case arrayTag:
            return cloneArray(target);

        case symbolTag:
            return cloneSymbol(target);

        case regexpTag:
            returncloneRegExp(target); }}function isObject(target) {
    const type = typeof target;
    returntarget ! = =null && (type === 'object' || type === 'function');
}

function deepClone(target, cache = new WeakSet(a)) {
    if(! isObject(target))return target; // Copy the base type value

    if (cache.has(target)) return target;

    cache.add(target);

    const tag = Object.prototype.toString.call(target);
    let cloneTarget = initCloneTargetByTag(target, tag); // Use the copy object constructor to create the corresponding type of data

    if (tag === mapTag) {
        target.forEach((value, key) = > {
            cloneTarget.set(key, deepClone(value, map));
        });
        return cloneTarget;
    }

    if (tag === setTag) {
        target.forEach(value= > {
            cloneTarget.add(deepClone(value, map));
        });
        return cloneTarget;
    }

    if (tag === functionTag) {
        return target;
    }

    Reflect.ownKeys(target).forEach(key= > {
        cloneTarget[key] = deepClone(target[key], cache); // Copy attributes recursively
    });

    return cloneTarget;
}
Copy the code

inheritance

The call to inherit

Call inheritance: Subclasses can only get the attributes of their parent class, not the methods of their prototype

function Parent(name) {
    this.parent = name;
}
Parent.prototype.say = function() {
    console.log('this.parent :>> '.this.parent);
}
function Child(name, parent) {
    Parent.call(this, parent); // Inherits the superclass attributes
    this.child = name;
}
Copy the code

Prototype chain inheritance

Subclass instances will share the same superclass prototype object and will affect each other

function Parent(name) {
    this.parent = name;
}
Parent.prototype.say = function() {
    console.log('this.parent :>> '.this.parent);
}
function Child(name, parent) {
    this.child = name;
}
Child.prototype = new Parent();
Copy the code

Combination of inheritance

The disadvantage of combining call inheritance with stereotype chain inheritance is that the Parent constructor is executed twice

function Parent(name) {
    this.parent = name;
}
Parent.prototype.say = function() {
    console.log('this.parent :>> '.this.parent);
}
function Child(name, parent) {
    Parent.call(this, parent); // Inherits the superclass attributes
    this.child = name;
}
Child.prototype = new Parent();
Copy the code

Parasitic combinatorial inheritance

None of the above disadvantages

function Parent(name) {
    this.parent = name;
}
Parent.prototype.say = function() {
    console.log('this.parent :>> '.this.parent);
}
function Child(name, parent) {
    Parent.call(this, parent); // Inherits the superclass attributes
    this.child = name;
}

Child.prototype = Object.create(Parent.prototype); // object.create creates a copy of the parent stereotype, completely isolated from the parent stereotype
Child.prototype.say = function() {
    console.log('this.child :>> '.this.child);
}
Child.prototype.constructor = Child;
Copy the code

new

For more details, please check out my interesting blog new

function selfNew(Ctor, ... args) {
    let instance = Object.create(Ctor.prototype);
    let res = Ctor.apply(instance, args);
    if (/^(object|function)$/.test(typeof res)) return res;
    return instance;
}
Copy the code

bind

For more details, please check out my blog interesting Talk bind

Function.prototype.selfBind = function(context, ... bindArgs) {
    if (typeof this! = ='function') {
        throw new TypeError('Parameter type error');
    }
    const self = this;
    let fBound = function(. args) {
        return self.apply(this instanceof self ? this : context, bindArgs.concat(args));
    }
    fBound.prototype = Object.create(this.prototype);
    return fBound;
}
Copy the code

call

What call does:

  • Take an object and set the function as its property
  • Execute the &delete function
  • The specifiedthisExecutes the function with the given argument
  • If no argument is passed, the default point iswindow
Function.prototype.selfCall = function (context, ... args) {
    context = context || window;
    const key = Symbol('key');
    context[key] = this;
    letres = context[key](... args);delete context[key];
    return res;
}
Copy the code

apply

It’s basically the same as the bind method, except that it accepts different types of arguments

Function.prototype.selfApply = function(context, args) {
    context = context || window;
    const key = Symbol('key');
    context[key] = this;
    letres = context[key](... args);delete context[key];
    return res;
}
Copy the code

instanceof

The instanceof operator is a Prototype property that checks for the presence of a constructor on the prototype chain of an instance object.

  1. First get the prototype of the current class, the prototype chain of the current instance object
  2. Always loop (performs the lookup mechanism of the prototype chain)
  3. Gets the prototype chain of the current instance object prototype chain (proto = proto.__proto__, looking up the prototype chain)
  4. If the current instance of the prototype chain__proto__The prototype of the current class was found onprototype, the returntrue
  5. If I keep findingObject.prototype.__proto__ == null.ObjectThe base class (nullIf none of the above is found, returnfalse
function selfInstanceof(left, right) {
    let proto = Object.getPrototypeOf(left);
    while (true) {
        if (proto === right.prototype) {
            return true;
        }
        if (proto === null) {
            return false;
        }
        proto = Object.getPrototypeOf(proto); }}Copy the code

Object.create

The Object. Create method essentially creates an empty constructor F, sets the f. protoType attribute to point to the argument Object proto, and returns an instance of F that inherits proto’s attributes.

Object.prototype.create = function(proto) {
    function F(){};
    F.prototype = proto;
    return new F();
}
Copy the code