What’s bind for? The bind() method creates a new function, and when bind() is called, this of the new function is specified as the first argument to bind(), and the remaining arguments will be used as arguments to the new function. Pre-knowledge:

  • Closure:
    • The external holds a reference to the function’s original definition scope, which is a closure (for example, the common asynchronous callback function scenario)
    • Resident memory, does not pollute globally and is not collected by garbage collectors (such as IIFE module mechanism)
  • This:
    • Before ES6, the this binding occurred on a function call, pointing neither to the function itself nor to the lexical scope of the function
    • ES6 has a new arrow function that does not follow the binding logic of function calls, but is determined by the outer (function or global) scope (lexical scope)

Why bind?

Classic interview questions:

var name = 'window',obj = { name: 'Jacky'.whatIsYourName: whatIsYourName }

function whatIsYourName(){ console.log(this.name) } 

obj.whatIsYourName() // 'Jacky'

var test = obj.whatIsYourName
test() // 'window', although test is a reference to obj.whatIsYourName, it actually refers to the whatIsYourName function itself.
Copy the code

When react collects the dom attribute callback function, change it to onClick, which needs to be reassigned to a temporary variable like var test = obj.whatIsYourName. It is possible to lose the reference to this during assignment, causing problems in finding variables, so bind is required.

Here’s another interview question:

var name = 'window',obj = { name: 'Jacky'.whatIsYourName: whatIsYourName }

function whatIsYourName(){ console.log(this.name) } 

function passParams(fn){ 
  // fn.bind(obj)() 'Jacky'
  fn()
}

passParams(obj.whatIsYourName) // 'window'
Copy the code

Passing the fn argument to execute causes this to be lost, which is common in all kinds of asynchronous callback functions, and is resolved by bind.

The realization of the bind

Function.prototype.apply = function(ctx,... args){
  const context = ctx || global
  const hash = +new Date(a)// Avoid the same name
  context[hash] = this // Cache this, call aplly and delete it
  constresult = context[hash](... args)// There is no difference between call and apply with extension operators
  delete context[hash]
  return result
}

Function.prototype.bind = function(context, ... args){
  // Error handling
  if(typeof this! = ='function') {throw new TypeError('invalid invoked! ')}// The closure's self field records the function that calls bind, and args records the predefined arguments
  var self = this
  return function F(. rest){
    // Returns the result of the function's execution
    // Determine whether the function is a constructor or a normal function
    // The constructor this instanceof F returns true to refer the this of the binding function to the instance, allowing the instance to get the value from the binding function.
    // When this refers to the window as a normal function, the result is false, and bind the function's this to context
    if(this instanceof F){
      // return new self(... args, ... rest)
      return New(self)(rest.concat(args))
    }
    return self.apply(context, rest.concat(args))
  }
}

The new operator typically goes through four steps
// 1. Create an object;
// 2. Assign the scope of the constructor to the new object (thus this refers to the new object)
// 3. Execute the code in the function (add new attributes to this object)
// 4. Return a new object
function New(f){
  return function(. args){
    var o = { __proto__: f.prototype } f.apply(o, ... args)return o
  }
}
Copy the code

Check the bind process through the debugger

Var test = obj.whatisyourName.bind (obj); var test = obj.whatisyourname.bind (obj);

After the first test bind is done, test is a closure where the variables context, args, and this are locked inside test. Then test() actually executes self.apply(context, rest.concat(args)). Pass in the cached data from the closure, apply uses context[hash] to dynamically record self, Function whatIsYourName(){console.log(this.name)}, const result = context[hash](… Args) calls the function that is actually bind

Function whatIsYourName(){console.log(this.name)} and contextobj = {name: ‘Jacky’, whatIsYourName: whatIsYourName}

var obj = { name: 'Jacky'.whatIsYourName: whatIsYourName }

function whatIsYourName(){ console.log(this.name) } 

Function.prototype.apply = function(ctx,... args){
  const context = ctx || global
  const hash = +new Date(a)// Avoid the same name
  context[hash] = this // Cache this, call aplly and delete it
  constresult = context[hash](... args)// There is no difference between call and apply with extension operators
  delete context[hash]
  return result
}

function apply(){
    var context = {
        '1611578238466': whatIsYourName,
        name: 'Jacky'.whatIsYourName: whatIsYourName
    }
    context['1611578238466'] ()// 'Jacky'
}
Copy the code

Contextobj = {name: ‘Jacky’, whatIsYourName: contextobj = {name: ‘Jacky’, whatIsYourName: Function whatIsYourName(){console.log(this.name)}; So this in the target function only wants to be in the context, changing the direction of this. Var test1 = test.bind(window) var test1 = test.bind(window)

Where context is the window object passed in, this refers to the caller test, which is the return value of the previous bind, written as f f (… Rest), and returns a new anonymous function f f (… Test1: self (rest) : self (rest) : self (rest);

Add context and self to Watch, you can clearly see that self refers to the anonymous function f f (… Rest), context refers to the passed parameter window, into the apply call:

According to the simplified apply call, the actual call is:

function apply(){
    var context = {
        '1611628513407': f F(... rest), ... The rest of thewindowAttribute} context ['1611628513407'] ()// Perform the result of the previous bind
}
Copy the code

When the last bind result is executed, due to the closure’s nature, test holds a reference to the function’s original definition scope. You can see that context and self become the arguments to the first bind, obj, and self becomes the first bind caller, f whatIsYourName(). The logic that follows is naturally the logic of the first bind, and the result of that first bind is returned.

So what happened to the window argument passed in the second time?

The answer is: The window acts as an intermediary for the mount function. If you pass in a function like function whatIsYourName(){console.log(this.name)} as you did the first time, This will refer to the window and change this, but we’ll pass in a closure that already holds a reference to the function’s original scope definition, so we’ll forget about the window and revert to the logic of the first bind.

To sum up:

  • The reason why call and apply can change this is that the this binding occurs when the function is called, and it does not refer to the function itself or the lexical scope of the function. When a function refers to a context object, the implicit binding rule will bind this in the function to the context object. That’s the context object in apply(). It is important to note that only the upper or last level in the object attribute reference chain is involved in the call.

  • Bind can bind this permanently because it takes advantage of the closure’s ability to cache the context object from the first bind, so that subsequent bind calls do not change the logic of the first call.

The implementation of the article is relatively rough, without considering a lot of boundary conditions, you will see, if you feel there is a harvest, do not be stingy with your small praise 👍