Prototype chain

  • Refer to JS prototype and prototype chain analysis

inheritance

  • Refer to several kinds of JS inheritance

Call, apply, bind usage and implementation

  • What it does: Change, in a nutshellthisTo call a method from one object to another

usage

  • applywithcallIs similar in that it executes the function directly
A.sayName.call(B,'tom')
A.sayName.apply(B,['tom'])
Copy the code
  • bindUsage andcallSimilar, but different, she doesn’t execute it immediately, she returns a copy of the original function
let bSay = A.sayName.bind(B,'tom') / / copy
bSay()/ / call
Copy the code

implementation

//A.myCall(B,arg1,arg2)

// Mount to Function's prototype object for inheritance to Function object calls
Function.prototype.myCall = function (originFun, ... args) {
	// myCall from A, where this refers to A function
    console.log(this)
    // Define A private property in B to point to function A
    originFun.__thisFn__ = this
    //B calls A, this refers to B
    letresult = originFun.__thisFn__(... args)// Delete useless attributes
    delete originFun.__thisFn__
    return result
}


Copy the code

Methods for determining types

  • The typeof and instanceof

  • Object.prototype.toString.call(); Every Object has a toString() method, which by default is inherited by every Object and returns [Object type] if it is not overridden. Type is the type of the Object. Such as:

    let obj= new Object()
    obj.toString() // "[object object]"
    Copy the code

    But for other cases

    var arr =new Array(1.2)
    arr.toString() / / output 1, 2, ""
    Copy the code

    This is because when the so-called Array, String and other types inherit from the base class Object, they rewrite the toString method. When calling, when searching up the method through the prototype chain, they stop searching and call the method directly when they find the toString above Array, so the output is different from the expected. How do I get arR to call this method on Object? Borrow call() for obvious reasons

    Object.prototype.toString.call(arr) // Output "[object Array]"
    Copy the code

    The slice method is then used to intercept the type

    // Whether it is a string
    export const isString = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='String'
    
    // Whether it is a number
    export const isNumber = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Number'
    
    / / a Boolean
    export const isBoolean = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Boolean'
    
    // Is a function
    export const isFunction = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Function'
    
    // Whether the value is null
    export const isNull = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Null'
    
    / / is undefined
    export const isUndefined = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Undefined'
    
    // Whether to object
    export const isObj = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Object'
    
    // Whether an array is available
    export const isArray = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Array'
    
    // Whether the time
    export const isDate = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Date'
    
    // Is regular
    export const isRegExp = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='RegExp'
    
    // Whether the object is an error
    export const isError = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Error'
    
    // Is the Symbol function
    export const isSymbol = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Symbol'
    
    // Whether the object is a Promise
    export const isPromise = (o) = > Object.prototype.toString.call(o).slice(8, -1) = = ='Promise'
    Copy the code

Capture and bubble

  • Event capture and bubbling process

capture

  • The event capture phase checks whether an event is registered, layer by layer, from the outside in

  • How do I register events during the capture phase?

    • useaddEventListenerRegisters the event, and the third parameter indicates whether the event is processed during the capture phase, set totrue
    <div class="box">
        <button class="btn">button</button>
    </div>
    
    <script>
        // display div first and then display BTN
        const box = document.querySelector('.box')
        box.addEventListener(
            'click'.() = > {
                alert('Div is clicked')},true
        )
        const btn = document.querySelector('.btn')
        btn.addEventListener(
            'click'.() = > {
                alert('Button is clicked')},true
        )
    </script>
    
    Copy the code

The bubbling

  • In contrast to capture, during the event bubbling phase, events are checked to see if they are registered from the inside out

  • By default, all events are registered in the bubbling phase. How do I prevent events from bubbling?

    • W3 standards

      event.stopPropagation()
      Copy the code
    • IE browser

      event.cancelBubble=true
      Copy the code

Event delegation

  • More often than not, multiple elements register the same event and can delegate those events to its parent elementulThe inside of theliTag click event
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

<script>
    const ul = document.querySelector('ul')
    ul.addEventListener('click'.(e) = > {
        if (e.target.nodeName == 'LI') {
            alert(e.target.innerText)
        }
    })
</script>
Copy the code

The closure problem

  • Access the scope of an outer function in an inner function

Isolation scope

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      returnprivateCounter; }}};// Counter1 and Counter2 do not affect each other. Modifying variables in a closure does not affect variables in the other closure
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

Copy the code

The classic for loop closure problem

for (var i = 1, arr = []; i < 5; i++) {
    setTimeout(() = > {
        console.log(i)
    }, 500)}/ / 5,5,5,5

// Use closures

for (var i = 1, arr = []; i < 5; i++) { ; (function (i) {
        setTimeout(() = > {
            console.log(i)
        }, 1000 * i)
    })(i)
}/ / 1, 2, 3, 4
Copy the code

What happens when you implement a new object

const myNew = (constructorFn, ... args) = > {
  // Create a new object
  let obj = {}
  // Create a private property that points to the constructor's prototype object
  obj.__proto__ = constructorFn.prototype// Execute the constructorconstructorFn.call(obj, ... args) // Return this objectreturn obj
}
function Person(name, age) {
  this.name = name
  this.age = age
}

const Per1 = new Person('Tom'.12)
console.log('Per1', Per1)
const Per2 = myNew(Person, 'Tom'.12)
console.log('Per2',Per2)
Copy the code

Anti-shake and throttling

  • The realization of anti-shake and throttling

Depth copy

  • Basic types ofString,NumberAre allAccording to the valueIts value is stored in the stack
  • Reference types are accessed by reference, which stores reference addresses on the stack and then creates space on the heap for their values

Shallow copy

  • If it is a primitive type, the value of the primitive type is copied directly; if it is a reference type, the reference address of the reference type is copied
  • Shallow copies do not affect each other for basic types, but for reference types, because they copy addresses that eventually point to the same heap, they affect each other

implementation

  • ES6 expansion operator
let obj1 ={name:'tom'.age:12.addr: {lng:26.0.lag:45.0},friends: ['john'.'jerry']}
letobj2={... obj1} obj2.name='jerry'
obj2.frinds[0] ='tom'
Copy the code

The ES6 expansion operator behaves differently for different structures of data. Deep copy for one-dimensional objects or arrays, shallow copy for multi-dimensional objects or arrays

  • Object.assign()
let obj1 ={name:'tom'.age:12.addr: {lng:26.0.lag:45.0},friends: ['john'.'jerry']}
let obj2= Object.assign({}, obj1);
Copy the code

Same thing as deconstructing assignments

  • lodash.clone()
var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);
Copy the code

Deep copy

  • For reference types, it creates a new space for objects so that the two deep-copied objects don’t interact

implementation

  • JSON.parse(JSON.stringfy() )
let obj1 ={name:'tom'.friends: ['jerry'.'john']}
let obj2 =JSON.parse(JSON.stringify(obj1))
obj2.name='jerry'
obj2.friends[0] ='tom'
Copy the code

  • lodash.cloneDeep()
var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
Copy the code
  • Recursive traversal implementation
function clone(targetObj) {
    // Check whether there are no objects, objects to traverse the copy
    if (typeof targetObj === 'object') {
        // Determine whether the source data is an object or an array
        let cloneTarget = Array.isArray(targetObj) ? [] : {}
        for (const key in targetObj) {
            // Recursive copy
            cloneTarget[key] = clone(targetObj[key])
        }
        return cloneTarget
    } else {
        // The object is not returned directly
        return targetObj
    }
}
Copy the code