Mind mapping

Hello everyone, I’m Lin Yiyi. Call, apply, bind, call, bind, call, bind, call, bind, call, bind 🧐

Call, apply, and bind can all change the direction of this

Check out this article for this orientation problemInterview | JS, you have to understand this point

[function]. Call ([this], [param]…) In a word:call()The function of thethisAssigned to thecall()The value of the first argument of the

[function].call([this]). Call () binds this to the first argument in function. This in call() refers to [function], which is executed inside the call() function. Call () executes the function by manipulating this. Pass the remaining arguments to [function] as well.

thinking

1

function fn(a, b) {
    console.log(this, a, b)
}

var obj = {
    name: 'Lin Yiyi'
}

fn.call(obj, 20.23)   // {name: "linyiyi "} 20 23

fn.call(20.23) // Number {20} 23 undefined

fn.call()   //Window {0: global, Window:... For undefined} undefined undefined | strict mode

fn.call(null)   //Window {0: global, Window:... Null} undefined undefined | strict mode

fn.call(undefined)  //Window {0: global, Window:... For undefined} undefined undefined | strict mode
Copy the code

Fn calls call, fn’s this points to obj, and fn is executed; Values referred to by this are reference types. In non-strict mode, this refers to window without passing arguments or null/undefined. The raw value is passed, and the raw value is wrapped. In strict mode, one argument to call refers to whoever it is

2

var obj1 = {
    a: 10.fn: function(x) {
        console.log(this.a + x)
    }
}

var obj2 = {
    a : 20.fn: function(x) {
        console.log(this.a - x)
    }
}

obj1.fn.call(obj2, 20) / / 40
Copy the code

In obj1.fn, fn’s this points to obj2, and finally executes the function in obj1.fn.

2. Apply and Call are basically the same

The only difference is that the first argument to apply is the reference to this, and the second argument is the array [arg1, arg2...]. The second argument to call is a list (arg1, arg2...).

var name = '二二'
var obj = {
    name: 'Lin Yiyi'.fn: function() {
        return `The ${this.name + [...arguments]}`
    }
}
obj.fn.apply(window[12.23.34.56])    // "212,23,34,56"
Copy the code

The second argument to apply accepts an array

The interview questions

1. Simulate the implementation of built-in call(), apply() methods.

  • A mock implementation of CALL

To simulate the implementation of call, we need to understand the principle of call. 1. The direction of this is changed, and the call function is executed in the call function. The following code reference comes from Amazed Feather

Function.prototype.myCall = function (context, ... args){
    context = context || window
    // This refers to fn, and we can use this to get fn. Context is our obj, and we can add a function property to obj
    context.fn = thiscontext.fn(... args)delete context.fn
    return
}

var name = '二二'
var obj = {
    name: 'Lin Yiyi',}function fn() {
    console.log(this.name, ... arguments) } fn.myCall(null)
fn.myCall(obj, 12.23.45.567)
Copy the code

The above mock call does not take into account the case of primitive types. Native call functions can also handle primitive types such as the above warm-up 1 fn.call(20, 23) output without error. However, myCall will directly report errors, providing a more comprehensive simulation of call can see thoroughly understand the closure, Currified, handwritten code, gold nine silver ten no longer lose points!

  • A simulated implementation of Apply
Function.prototype.myApply = function (context, args){
    context = context || window
    context.fn = thiscontext.fn(... args)delete context.fn
    return
}
Copy the code

Similar to the analog call writing above

2. There is a difference between call and apply

The syntax and function of the call method are similar to those of the apply method, except that the call() method accepts a list of arguments, whereas the Apply () method accepts an array of arguments.

var name = '二二'
var obj = {
    name: 'Lin Yiyi'
}

function fn(){
    console.log(this.name, ... arguments) } fn.apply(obj, [12.34.45.56]) //fn(12, 23, 45, 56) Lin Yiyi 12 34 45 56
Copy the code

Note that the remaining array arguments are passed to the function as a single argument, fn.apply(obj, [12, 34, 45, 56]) ==> fn(12, 23, 45, 56).

Third, the bind

To quote MDN: The bind() method creates a new function. When the new function is called, the first argument to bind() will be this when it runs, and the subsequent sequence of arguments will be passed as its arguments before the arguments passed.

  • Returns a new function when the new function is executedthisDidn’t specify thebindThe first argument to
  • bindIs passed to the new function
  • The returned function is self-calling and can pass in new arguments

Small thinking

1. What is this new function?

Bind returns a copy of the function that called bind.

A little chestnut

var name = '二二'
var obj = {
    name: 'Lin Yiyi'
}

function fn(){
    return `The ${this.name} ` + [...arguments]
}

let f = fn.bind(obj, 12.23.45.67.90)
f() // "Lin yiyi 12,23,45,67,90"
Copy the code

The new function above is f(), which is returned by bind’s copy of fn.

2. How can BIND copy FN?

Simply put: Get this and return the function that this gets, as in call.

The interview questions

1. Bind () is different from call() and apply()

The first argument to apply and call is the same as the object to be changed. The second argument, apply, is an array, and call is arg1,arg2… In this form. Changing the this scope through bind returns a new function that will not be executed immediately

2. Mock implementation of the built-in bind() method.

  • Simplified version implementation
Function.prototype.myBind = function myBind(context, ... arg) {
    var _this = this    // Get the function body that calls myBind
    function ctor (. otherArg){      // The new function returned is a copy of the call to myBind
    // This instanceof ctor is true, indicating that an instance is returned.
    // The new keyword is used.
        _this.call(this instanceof ctor ? this: context, ... arg.concat(... otherArg))// Use the apply principle to change this pointer and execute the new function returned.
    }
    
// Change the return function's prototype to the binding function's prototype, and the instance inherits the value from the binding function's prototype
    ctor.prototype = this.prototype
    return ctor
}

var obj = {
    name: 'Lin Yiyi'
}

function fn(){
 console.log(this.name, ... arguments) }var a = fn.myBind(obj, 12.23.34.56)
a() // Lin Yiyi 12 23 34 56
Copy the code
  • For a more comprehensive version, the following code comes fromJavaScript in-depth simulation of bind implementation
Function.prototype.bind2 = function (context) {

    if (typeof this! = ="function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments.1);

    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}
Copy the code

Simulation API implementation, the most important is the thought process, which is not copy.

Four, think about the question

1. Find the maximum and minimum values in the array

  • Find the maximum and minimum values using Max /min of Math

    let arr = [12.45.65.3.23.11.76.8.9.56.70]
    let max = Math.max(... arr)/ / 76
    let min = Math.min(... arr)/ / 3
    Copy the code
  • Find the maximum and minimum values using the array sort method

    let arr = [12.45.65.3.23.11.76.8.9.56.70]
    let list = arr.sort(function(a, b) {
        return b - a
    })
    let max = list[0]   / / 76
    let min = list[list.length - 1] / / 3
    Copy the code
  • Use apply to find the maximum and minimum value of the array

    let arr = [12.45.65.3.23.11.76.8.9.56.70]
    let max = Math.max.apply(null, arr) / / 76
    let min = Math.min.apply(null, arr) / / 3
    Copy the code

2. How to determine an array

Object. The prototype. ToString. Call (), instanceof. Note in particular that Typeof cannot determine array types

let arr = []
Object.prototype.toString.call(arr)
Copy the code

3. The Object. The prototype. ToString. Why call () can be used to determine the type

Because the Object. The prototype. The toString () method returns the Object of type string, output “[Object Object]” the second Object is the constructor of the incoming parameters. So you can use call to specify any value and return the resultant constructor type with toString to determine the type. Apply /bind can do the same thing

Object.prototype.toString.call('str')   // "[object String]"
Object.prototype.toString.call(123)   // "[object Number]"
Object.prototype.toString.call({})      // "[object Object]"
Object.prototype.toString.call([])      // "[object Array]"

Object.prototype.toString.apply({})      // "[object Object]"
Object.prototype.toString.apply([])      // "[object Array]"

var f = Object.prototype.toString.bind({})
f()     // "[object Object]"
var fn = Object.prototype.toString.bind([])
fn()   // "[object Array]"
Copy the code

4. Use the Call () implementation to convert the class array to an array

Call (), [].slice/array.prototype.slice ()

let array = [12.23.45.65.32]
function fn(array){
    var args = [].slice.call(arguments)
    return args[0]
}
fn(array)   // [12, 23, 45, 65, 32]
Copy the code

Call was used above to change slice’s this to arguments to iterate over the output.

reference

JavaScript deep simulation of call and apply

JavaScript in-depth simulation of bind implementation

MDN bind

The end of the

Thank you for reading this, if this article can enlighten or help you a little bit, welcomestarThis is Lin Yiyi. See you next time.