Hello, I’m a mouse

Big brother big sister come first don’t go ah, rest assured, this article is full of dry goods! Do you know all these things?

Don’t walk ah don’t walk ah, even if the meeting, also might as well glance, in case there are hundreds of millions of small details worth learning? (Ow O)/~

preface

Feeling like a string of articles received pitiful viewership, that was one way to remain “one hundred and twenty” readers.

This article summarizes the most important and common Javascript knowledge points and addresses each point:

  • Basic introduction
  • Principle and implementation steps
  • Handwritten implementation

Hope to be able to through the analysis of knowledge points and realization, and then add hundreds of millions of details, sublimation of its understanding, to ask for a praise πŸ‘πŸ‘πŸ‘ crazy hint!

“Be sure to let me know if your articles are” remaining “ones

You can refer to the directory to select their own part of the need to read, the content will be updated from time to time, suggested collection! (Do me a favor.)

1. Write new method by hand

The principle of

First of all, the new method mainly implements the following functions:

  • Create an empty object (prototypeFrom theObject.prototype )
  • Distinguish constructor from other parameters
  • Set the object prototype
  • Call the constructor on the object
  • If the constructor returns an object, it returns that object, otherwise it returns a new object

We can use the __proto__ attribute for the part of the above functionality that involves the prototype chain, but the official recommendation is object.getProtoTypeof and Object.setProtoTypeof.

Each function has a prototype object; constructor is the attribute that the prototype object points to.

implementation

let newObject = function() {
    let obj = new Object(),
        Constructor = Array.prototype.shift.call(argument);
    // obj.__proto__ = Constructor.prototype; / / abandoned
    Object.setPrototypeOf(obj, Constructor.prototype);
    
    let ret = Constructor.apply(obj, arguments);
    return typeof ret === 'object' ? ret : obj;
}
Copy the code

If you are familiar with ES6, the above code can actually be optimized with the remaining parameters.

  • Extract constructor and other parameters

    function (constructor, ...arguments){}
    Copy the code
  • Merge the newly created object operation

    Object. Create can replace the three lines in this article. See the implementation next

    Object.create(constructor.prototype);
    let ret = constructor.apply(obj, arguments);
    
    / / equivalent to the
    
    let obj = new Object(),
        Constructor = Array.prototype.shift.call(argument);
    // obj.__proto__ = Constructor.prototype; / / abandoned
    Object.setPrototypeOf(obj, Constructor.prototype);
    let ret = Constructor.apply(obj, arguments);
    Copy the code

2. Write the object.create method

In the last example, we used an Object.create, so let’s talk about it now.

The main function is to generate a new object from an existing object as a prototype object, and this new object’s __proto__ attribute points to the prototype object.

If null is used as an argument, since NULL is the end of the stereotype chain, it will return an empty object (no stereotype chain).

The principle of

  1. First we need a container to incubate new objects,let F = function(){}.
  2. Then we configure the container to generate the object we want,F.prototype = argsObject.
  3. Generate the objects we need,return new F().

implementation

function ObjectCreate(proto) {
    let F = function(){};
    F.prototype = obj;
    return new F();
}
Copy the code

3. Inheritance of handwriting classes (prototype chain and parasitic combination inheritance)

Object. Create ();

Prototype chains and inheritance are something that every Web front-end developer can’t escape, and now we’re going to implement inheritance using the Object. Create method we just described.

The principle of

There are many ways you can implement inheritance in javascript, but the best use of resources is still parasitic combinatorial inheritance.

Javascript inheritance is realized through the prototype chain, each object (including functions) has a prototype object, the subclass can read the prototype object of the parent class and the ancestor class, so as to access the methods of the parent class and the ancestor class, to achieve the purpose of inheritance.

Specific see the little Red book, here will not elaborate ~

The steps are as follows:

  1. Building subclass instance properties from the superclass constructor: The subclass borrows the superclass constructor, creates superclass instance properties on the instance, and adds new subclass properties.
  2. Inherit superclass attributes: Create a stereotype object based on a superclass stereotype (to prevent creating a superclass instance and creating redundant superclass instance attributes), bind the subclass constructor to the stereotype.
  3. Enhance the newly created stereotype object: Add subclass stereotype properties and methods:.

This is how class extends extends related keywords work in ES6.

implementation

function Super(name) {
    this.name = name;
    this.colors = ["red"."blue"];
}
Super.prototype.sayName = function() { console.log(this.name); };

function Sub(name, age) {
    Super.call(this, name);
    this.age = age;
}

(function inheritPrototype(Sub, Sup) {
    // Create a subclass prototype object based on the superclass prototype object, avoiding the creation of superclass instances and redundant attributes
    let prototype = Object.create(Sup.prototype);
    // Bind the relationship between the new prototype and the subclass constructor
    prototype.constructor = Sub;
    Sub.prototype = prototype;
})(Sub, Super);

// After binding the prototype, bind the new method to the new prototype object
Sub.prototype.sayAge = function() { console.log(this.age); }
Copy the code

4. Handwritten arrow function

The principle of

Arrow functions are one of the most important new features in ES6. Arrow functions have no arguments, prototype, caller, or even their own this. Instead, they bind the most recent this in the lexical execution field.

Therefore, arrow functions cannot be used as constructors, and cannot be used with new. .

The arrow function still has the prototype chain of the function object, but it cannot bind this when calling call and apply and is ignored.

The arrow function is essentially an enhancement of our earlier ES5 binding scope:

function () {
    let that = this;
    setTimeout(function() {
        return that;
    }, 1000);
}
Copy the code

Babel escapes es5 implementation

The implementation of the arrow function is essentially the usual way we pass the this pointer, creating a closure to hold the reference.

function A() {
    return () = >{console.log(this)}
}

funcion funA() {
    return (function(){console.log(this); }).bind(this);
}
Copy the code

5. Write call and apply functions by hand

Function.prototype.call and function.prototype. apply

In ES6, these two methods are included in Reflext as reflect. call and reflect. apply, which work the same way.

Call and apply are both used as secondments (change the this pointer and call it immediately):

Array.prototype.join.call(["1", "2"], " ")
Copy the code

The above representation says that.split(” “) is called when this points to (that is, the array [“1”, “2”])

func.apply(thisArg, [argsArray])
Copy the code

Parameter 1: The function takes a this, binding the function execution field;

The biggest difference between Call and Apply is that Call accepts a list of parameters, while Apply accepts an array of parameters.

The principle of

First, call and apply are both methods on Function prototypes that we need to define in function.prototype

Secondly, the difference between the two is the difference of extraction parameters, which is mainly implemented by call.

  1. Check whether it is a function or not
  2. Separate the parameters in the parameters
  3. Identify secondment targets
  4. Create this method at the secondment target (link scope)
  5. Execute and save the result
  6. Delete the created method
  7. Returns the result

implementation

Function.prototype.call = function (thisArg) { 
    
    // Check whether the current party is a function (this is Product, check whether Product is a function)
    if (typeof this! = ='function') {
        throw new TypeError('The current call method is not a function! ');
    }
    
    // Get argument list if apply [1]
    const args = [...arguments].slice(1);
    
    // If thisArg is null or undefined
    thisArg = thisArg || window;
    
    // Create a unique value
    const fn = Symbol('fn');
    
    // Add this method to the target
    thisArg[fn] = this;
    
    // Execute the saved function, in which case the scope is executed within the scope of the target object
    constresult = thisArg[fn](... args);// Delete the value of the newly added attribute
    delete thisArg[fn];
    
    // Returns the execution result
    return result;
}
Copy the code

Const args = arguments.slice(1) if apply;

6. Write bind

Bind is based on apply and call, and I don’t want it to be called directly, I want it to be called when I need it.

The principle of

Bind this to and return a function that calls Call/apply when executed.

  1. Save the original function reference, i.ethis
  2. Prevent error reporting and extraction of parameters
  3. Return a function
  4. Returns a function that takes a quadratic argument
  5. newThe operation priority is greater thanbind, in view of thenewCall extra judgment

implementation

1. Basic implementation ideas

Function.prototype.bind1 = function(context, ... args){
    var self = this;
    return function(){
        returnself.apply( context, args); }};Copy the code

2. Add error reporting and parameter extraction

Arguments are extracted twice: the arguments passed in when bind is called, and the arguments passed in when the return function is called

Function.prototype.bind2 = function (context) {
    if (typeof this! = ='function') {
        throw new Error('Function.prototype.bind - what is trying to be bound is not callable')}let f = this;
    let agrs = [...arguments].slice(1) // Take the argument for the first time
    let bindFun =  function () {
        let agrs2 = [...arguments]  // Take the second parameter
        let Allagrs = agrs.concat(agrs2)    // Merge two groups of parameters (note the order)
        f.apply(context, Allagrs)  // Use apply to change this to s
    }
    return bindFun
}
Copy the code

3. Call judgment for new

If called, this points to the newly created object

Function.prototype.bind3 = function (context) {
    if (typeof this! = ='function') {
        throw new Error('Function.prototype.bind - what is trying to be bound is not callable');
    };
    
    let f = this;
    
    let agrs = [...arguments].slice(1); // Take the first argument (the argument passed when calling bind2)
    
    let bindFun =  function () {
        let agrs2 = [...arguments];
        let Allagrs = agrs.concat(agrs2);
        
        // If the function is called, this points to the newly created object
        f.apply(this instanceof f ? this : context, Allagrs);
    }
    bindFun.prototype = f.prototype // Prototype chain inheritance
    return bindFun
}
Copy the code

7. Write a more comprehensive Typeof

Typeof is a common type determination operator in javascript, but it has drawbacks.

We can use typeof to determine number, String, object, Boolean, function, undefined, symbol, bigint.

This is based on the fact that when javascript stores variables at the bottom level, it stores their type information in the lowest 1-3 bits of the variable’s machine code, roughly as follows:

  • 000: object
  • 010: floating point number
  • 100: string
  • 110: a Boolean value
  • 1: the integer
  • -2^30: undefined

Where all similar objects (Array, etc.) and NULL (all zeros, ending with 000 as an object) return object

For this reason, we generally use toString to determine objects.

The principle of

Based on the Object. The prototype. ToString secondment to achieve:

According to the type implementation given by MDN, the judgment steps are as follows:

  1. Check whether the value is null or undefined

  2. To prevent over-interpretation, elements like HTMLDivElement are retrieved and then filtered using regular

    deepType.match(/^(array|bigint|date|error|function|generator|regexp|symbol)$/)

  3. Normal types are returned directly using Typeof

implementation

There are two inconsistencies in the implementation given by MDN:

  • Here,fullClassPersonally, it feels a little redundant
  • Why judge one last timetypeof obj === 'function'Before,matchI think we got a match.

The likelihood knowledge is shallow, if have big guy to see, hope can give a solution.

MDN version

function type(obj, fullClass) {
    if (fullClass) {
        return (obj === null)?'[object Null]' : Object.prototype.toString.call(obj);
    }
    if (obj == null) { return (obj + ' ').toLowerCase(); }

    var deepType = Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
    if (deepType === 'generatorfunction') { return 'function' }

    return deepType.match(/^(array|bigint|date|error|function|generator|regexp|symbol)$/)? deepType : (typeof obj === 'object' || typeof obj === 'function')?'object' : typeof obj;
}
Copy the code

8. Instanceof handwriting

This is a way to determine if an instance belongs to a type.

Too often I will not say nonsense, directly on the principle.

The principle of

Judgment basis:

  1. Symbol.hasinstance can override the instanceof behavior (by default, referring to the prototype chain), defined in the constructor:

    class MyArray {
      static [Symbol.hasInstance](instance) {
        return Array.isArray(instance); }}console.log([] instanceof MyArray); // true
    Copy the code

    We can even go a little overboard:

  2. The Array implementation of different Windows may be different, you can use array. isArray to determine

  3. Prototype chain

  • By instance__proto__Or useObject.getPrototypeOfGet the prototype object
  • Compare types by stereotype object, or stereotype objectconstructorComparison of type
  • If not, continue to query the prototype chain, i.e__proto__.__proto__Until the end of the prototype chainnull

implementation

function myInstanceof(instance, object) {
    if (object[Symbol.hasInstance]) return object[Symbol.hasInstance](instance);
    else {
        if (instance === null|| instance ! = ='object') return false;
        if (object === Array) return Array.isArray(instance);

        let proto = Object.getPrototypeOf(instance);
        while(proto ! = =null) {
            if(proto == object.prototype) return true;
            proto = Object.getPrototypeOf(proto);
        }
        return false; }}Copy the code

9. Handwritten deep copy

A shallow copy is a copy of a reference, and a deep copy is a copy of a value:

  • Shallow copy
let obj = {a:1};
let obj2 = obj;
obj2.a = 2;
console.log(obj); // {a:2}
Copy the code

In fact, obj and obj2 point to the same memory region identifier, so the changes are the same.

Object.assign(A, B) adds all references from B to A;

  • Deep copy

All deep copy does is copy the values and break the references.

let obj = {a:1};
let obj2 = {};
obj2.a = obj.a;
obj2.a = 2;
console.log(obj); // {a:1}
Copy the code

The principle of

There are two ways to do this. One is to convert the array to a string and then rebuild the array from the string.

Another way is to loop recursively, copying the structure of the object.

For cyclic recursion, we need to prevent:

  1. A circular reference
  2. Multiple copies of the same reference object

The steps are as follows:

  • Record objects iterated through arrays (before copy, after copy)
  • Check whether it is an object. If it is an object, perform deep copy

implementation

  1. JSON. Parse and JSON. Stringify
  • Properties with values of function, re, and Symbol are not copied
  • This can lead to circular references
  • The same reference is copied multiple times, that isa.a = a.b = {}But copy the resultsa.a ε’Œ a.bfor{}But not equal to
  1. Cyclic recursive copy
function deepClone(obj) {
    let copyed = new Map(a);function _deepClone (obj) {
        if (obj && typeof obj === "object") {
            
            let objClone = Array.isArray(obj) ? [] : {};
            if(! copyed.has(obj)) copyed.set(obj, objClone);for ( key in obj ) {
                if( obj.hasOwnProperty(key) ) {
                    if ( obj[key] && typeof obj[key] === "object" ) {    
                        if (copyed.has(obj[key])) objClone[key] = copyed.get(obj[key]);
                        else objClone[key] = _deepClone(obj[key]);
                    }else{ objClone[key] = obj[key]; }}}return objClone;
        } else return obj;
    }
    return _deepClone(obj);
}
Copy the code

10. Handwritten array deduplication

The principle of

Based on the Object. The prototype. ToString. Call.

We need to judge:

  • Symbol(“”= symbol (1)), but does not participate in the operation, so “”= symbol (1) implicit conversion error

    To evaluate the same value Symbol, call Symbol(1).tostring ()

  • object

    Content is not judged heavy, judge whether there has been the same reference

    Content reweighting is only available for objects json.stringify

  • NaN is judged separately and uniquely by number. isNaN and hasNaN tokens

  • Other numerical

    Including null and undefined

    Use temp to store Typeof + String(obj) as key values, judge the weight

implementation

Array.prototype.uniq = function () {
    if (!this.length || this.length == 0) return this;
    var res = [], key, hasNaN = false, temp = {};
    for (var i = 0 ; i < this.length; i++) {
        if (typeof this[i] === 'symbol') {
            res.push(this[i]);
        } else if (Object.prototype.toString.call(this[i]) === '[object Object]') {
            key = typeof(this[i]) + JSON.stringify(this[i]);
            if(! temp[key]) { res.push(this[i]);
                temp[key] = true; }}else if (Number.isNaN(this[i])) { // If the current traversal element is NaN
            if(! hasNaN) { res.push(NaN);
                hasNaN = true; }}else {
            key = typeof(this[i]) + this[i];
            if(! temp[key]) { res.push(this[i]);
                temp[key] = true; }}}return res;
}
Copy the code

11. Handwriting array flat

Flattening an array is expanding a nested array:

The principle of

We need to pass in a parameter n to control the number of layers to unfold,

Here I just use the method is recursive, of course I believe you must have a better ~

  1. Use n to control the number of recursive levels, nativeflatThe default is 1
  2. Determines if the maximum level of recursion has been reached, and if so, returns the element directly
  3. Determine whether the current item is an array or other data
  4. Use recursivereduce 与 concatConcatenate and return the result

implementation

function flatArray(arr, n = 1) {
    function deep(item, i) {
        if (i === n) {
            return item;
        }
        else {
            if (Array.isArray(item)) {
                return item.reduce((prev, cur) = > {
                   return prev.concat(deep(cur, i+1)); } []); }else returnitem; }}return deep(arr, 0);
}
Copy the code

11. Handwriting anti-shock function debounce

The so-called anti-shake function, as the name suggests, prevents wobbly legs from shaking!

This function is mainly used to prevent events from being triggered frequently. No matter how many times events are triggered, the task can be executed only after the interval of the last time.

Commonly used for onMousemove, onresize, search input box and other high-frequency trigger events:

The principle of

  1. Set a timer
  2. Trigger time for the first time, activate a timer
  3. The timer is reset when events are triggered for as long as the timer is valid.
  4. The last trigger and interval of a certain time, the timer ends, trigger the event;
  5. Clear the timer.

This should require the use of closures to store timers.

implementation

function debounce(fn, delay) {
    let timer;
    return function (. args) {
        var _this = this; Save the scope of the created functionif (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(function () {
            fn.apply(_this, args);
            clearTimeout(timer);
        }, delay);
    };
}
Copy the code

12. Handwritten throttle function throttle

The throttling function is similar to the anti – shake function, but different.

The throttling function is a bit like a game character’s skill and can only be used once at a given time.

To put it another way, cool the CD.

As the name suggests, throttling is often used to control traffic by controlling how often network requests are sent, such as login authentication and searches.

The principle of

There are two implementations, both time-based in nature.

  1. Timer ⏲ timing
  2. Timestamp timing

Similarly, closures are needed to determine if the cooling is complete and can be executed.

After each execution event, set a timer. During the timing period, the execution time cannot be set.

implementation

  1. The timer
function throttle(func, delay = 100) {
    let canRun = true;
    return function (. args) {
        let _this = this;
        if(! canRun) {return;
        }
        func.apply(_this, args);
        canRun = false;
        setTimeout(function () {
            canRun = true;
        }, delay)
    }
}
Copy the code
  1. The time stamp
function throttle(func, delay = 100) {
    let previous;
    return function (. args) {
        let _this = this;
        let now = +new Date(a)if (now - previous <= delay ) {
            return;
        }
        func.apply(_this, args);
        previous = +new Date();
    }
}
Copy the code

13. Write the sleep function

What is the sleep function? The short answer is __ Varudo

That is stop time!

We need to have our tasks executed at certain intervals, or stopped for n seconds.

To be clear, JavaScript is single-threaded, but setTimeout is implemented based on a browser module that starts a timer, each of which has no effect on the other.

So the above code will eventually pop up together.

A compatible solution is to use an increment timer:

setTimeout(func(), 100); setTimeout(func(), 200);

Or the callback function call:

setTimeout(()=>{func(); setTimeout(func(), 100); }, 100);

But that’s not really elegant,

It’s time to get back to the sun and call async/await (generator and promise syntax candy).

The principle of

We need to implement a sleep function for real time and return a promise

The external async execution function waits for the sleep with await

In order to achieve the purpose of time stop.

implementation

function sleep(delay) {
    return new Promise((resolve) = >{
        setTimeout(() = >resolve(), delay);
    });
}
Copy the code

Of course, it is important to note that the sleep is not on time, and it can be delayed by looking at the event loop queue.

conclusion

Thank you so much for watching, your likes are the best support for me!

Keep learning! I’ll be back ~ what do you think in the comments section ~

I will update this article from time to time in the future, if you feel good, remember to bookmark, something is ok, review the old to know something new ~

Refer to the article

  • MDN
  • Introduction to the Object. The prototype. ToString. Call () method
  • JS deep copy summary
  • Thoroughly understand function tremble and function throttling