role

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.

Look at the code:


// Assign the write method of document
let altwrite = document.write

altwrite('hellow') // Uncaught TypeError: Illegal invocation

Copy the code

This points to a Document object, assigned to Altwrite, which executes without a call. This points to a Global or window object.

If you want this of the Altwrite method to also point to a document object, you can also use CAL () and apply() described earlier.


// Assign the write method of document
let altwrite = document.write

altwrite.bind(document) ("hello")

altwrite.call(document."hello")

Copy the code

Bind () returns a function that points this in Altwrite to the document object and passes in arguments to execute; CAL () points this in Altwrite to the Document object, which passes in the argument directly using the Altwrite method and returns the executed value.

CAL (), apply(), bind()

1. Similarities:

  • Both are used to change the direction of the function’s this object;

  • The first argument is the object to which this refers, that is, the context that you want to specify;

  • Both can be passed with subsequent arguments, the same as CAL () and bind().

2. The difference between:

  • Bind () returns the corresponding function for later call; CAL () and apply() are called immediately and return the value after the function is executed.

  • CAL () and bind() do not take the first argument; the method’s this is bound as a global object. In strict mode, the value of this will be undefined.

    Bind () If the argument list of bind is empty, or thisArg is null or undefined, this executing the scope will be treated as the first argument to the new function.

  • Bind () constructs the binding function using the new operator and ignores the first argument.

The instance

The bind() method can also be used where CAL () is used, as above, but bind() returns functions and has its own use.

1. Bind functions

  1. The simplest use of bind() is to create a function that has the same value no matter how it is called. A common mistake, like the example above, is to take a method out of an object and call it, expecting this to point to the original object.

    
    let n = 0
    
    let a = {
      n: 1.getN: function() { 
        console.log(this.n)
      }
    }
    
    let b = { n: 2 }
    
    a.getN() / / 1
    
    let getNc = a.getN
    getNc() // 0 this points to the global object
    
    let getNcc = a.getNc.bind(a)
    getNcc() / / 1
    
    b.getNcc() // 1 This refers to the first argument of bind
    
    Copy the code
  2. Before ES6, we used _this, that, self, etc., to save this and refer to it after changing the context

    
    let a = {
      n: 1.getN: function() { 
        let _this = this
        $('.xx').on('click'.function (event) {
          console.log(_this.n)     / / 1}}})Copy the code

    This can be avoided by using the arrow function in ES6, which is the first method to use.

    
      $('.xx').on('click'.function (event) {
         console.log(this.n)     / / 1
      }).bind(this))
    
    Copy the code

2. Cooperate with setTimeout

This is similar to the second one above. In general, setTimeout() ‘s this points to a window or global object.


  function Person (name) {
    this.name = name
  }

  // Call getInfo after 1 second
  Person.prototype.print = function () {
    window.setTimeout(this.getInfo.bind(this), 1000)
  }

  Person.prototype.getInfo = function () {
    console.log('My name is' + this.name)
  }

Copy the code

3. The partial functions

Another simplest use of bind() is to make a function have default initial arguments. Just write these arguments (if any) after this as arguments to bind(). When the binding function is called, these parameters are inserted at the beginning of the argument list of the target function, followed by the parameters passed to the binding function.


function list() {
  return Array.prototype.slice.call(arguments);
}

function addArguments(arg1, arg2) {
    return arg1 + arg2
}

var list1 = list(1.2.3); / / [1, 2, 3]

var result1 = addArguments(1.2); / / 3

// Create a function with a list of default arguments.
var leadingThirtysevenList = list.bind(null.37);

// Create a function with a default first argument
var addThirtySeven = addArguments.bind(null.37);

var list2 = leadingThirtysevenList();
/ / [37]

var list3 = leadingThirtysevenList(1.2.3);
// [37, 1, 2, 3]

var result2 = addThirtySeven(5);
// 37 + 5 = 42

var result3 = addThirtySeven(5.10);
// 37 + 5 = 42, the second argument is ignored

Copy the code

4. Bind functions as constructors

Binding functions are automatically adapted to use the new operator to construct a new instance created by the target function. When a binding function is used to build a value, the supplied this is ignored. However, the supplied argument list is still inserted before the argument list at the time of the constructor call.

What do you mean? Let’s start with a chestnut:


function Person (name, age) {
  this.name = name
  this.age = age
  
  this.getInfo = function () {
    console.log(this.name + this.age)
  }
}

let p1 = new Person('xuxu'.18)
p1.getInfo() // xuxu18

Copy the code

Use the bind () :


let obj = {
  age: 19
}

let newPerson = Person.bind(obj, 'x')
let p2 = new newPerson(5)
p2.getInfo() // x5

Copy the code

According to the use of bind(), newPerson’s this points to obj, but the supplied this is ignored when constructed with the new new operator, so this points to P2.

5. A shortcut

Bind () can also create shortcuts for functions that require a particular this value.

Take the example of an array-like object converted to an array, of course, using the ES6 extension described earlier is the best


Array.prototype.slice.call(arguments)

Copy the code

But if you have a lot of array-like objects, you’ll need to write a lot of these expressions, so you can use bind()


let slice = Function.prototype.call.bind(Array.prototype.slice)

slice(arguments)

Copy the code

Code implementation (pre-ES6)

  1. Let’s start by saying that this is implemented using the pre-ES6 method, which simplifies things a little bit.

  2. Use call() and apply(), and if you don’t use call, implement a call yourself and replace it.

Here’s an example:


let a = b.bind(obj, '1'.'2')

a('3')

Copy the code

The first step

Change this to return a function to be executed later.

  1. The first element in arguments is the first argument obj that we pass in when we call bind. Use apply to call a and refer this from A to obj.

    
    Function.prototype.myBind = function () {
        
      return this.apply(arguments[0])}Copy the code
  2. Since bind does not execute immediately but returns a function, it returns it wrapped in a function. (Arrow functions can be used in ES6)

    
    Function.prototype.myBind = function () {
      let self = this
    
      return function () {
        return self.apply(arguments[0])}}Copy the code
  3. Finally, check whether a is function, and you’ll be perfect

    
    Function.prototype.myBind = function () {
      if (typeof this! = ='function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}let self = this
    
      return function () {
        return self.apply(arguments[0])}}Copy the code

This completes the first step perfectly: change this to return a function

The second step

When you call bind(), you may pass in other arguments ‘1’, ‘2’ in addition to the object to which you want to bind this. The arguments class array object is truncated from obj and converted to an array (ES6 can use […arguments]), and the resulting args is passed to apply.


Function.prototype.myBind = function () {
  if (typeof this! = ='function') {
    throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}let self = this
  let args = Array.prototype.slice.call(arguments.1)

  return function () {
    return self.apply(arguments[0], args)
  }
}

Copy the code

The third step

It is also possible to pass in arguments when using the a function, so you need to merge the passed arguments into the second argument of apply().

When we use function A, it’s actually the function that returns, i.e


return function () {
  return self.apply(arguments[0], args)
}

Copy the code

So, just spell the arguments of this function after args. (ES6 is simpler)


Function.prototype.myBind = function () {
  if (typeof this! = ='function') {
    throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}let self = this
  let thatArgs = arguments[0]
  let args = Array.prototype.slice.call(arguments.1)

  return function () {
    Array.prototype.push.apply(args, Array.prototype.slice.call(arguments))
    return self.apply(thatArgs, args)
  }
}

Copy the code

At this point, you’ve basically implemented bind in the chestnut above.

The fourth step

Binding functions are automatically adapted to use the new operator to construct a new instance created by the target function. When a binding function is used to build a value, the supplied this is ignored. However, the supplied argument list is still inserted before the argument list at the time of the constructor call.

This step mainly does this, which means using new to construct a new instance created by the target function


let newPerson = Person.bind(obj, 'x')

console.log(newPerson) F Person() returns the Person function

let p2 = new newPerson()

Copy the code

Ignore obj in bind(), and this points to the instance p2 that was created.

  1. As you can see, the only fBound function that returns is the one that called bind(), the Person above, with this and its arguments bound at execution time. By definition, when new, this refers to the created instance, so when the returned fBound is new, this refers to the instance. The returned Person’s this points to the real column, otherwise to obj.

    With the new keyword, P2 “inherits” the instance from Person.prototype, so we change fBound’s prototype to Person’s prototype.

    
    Function.prototype.myBind = function () {
      if (typeof this! = ='function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}let self = this
      let thatArgs = arguments[0]
      let args = Array.prototype.slice.call(arguments.1)
    
      let fBound  = function () {
        Array.prototype.push.apply(args, Array.prototype.slice.call(arguments))
        return self.apply( this instanceof fBound ? this : thatArg, args)
      }
    
      fBound.prototype = self.prototype
    
      return fBound
    }
    
    Copy the code
  2. Prototype (newPerson.prototype = {}); fBound. Prototype (newPerson.prototype = {}) Therefore, we need an intermediate variable fNOP equal to an empty function to maintain the prototype relationship and make fbound. prototype and Person.prototype no longer refer to the same prototype function:

    
    Function.prototype.myBind = function () {
      if (typeof this! = ='function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}let self = this
      let thatArgs = arguments[0]
      let args = Array.prototype.slice.call(arguments.1)
    
      let fBound  = function () {
        Array.prototype.push.apply(args, Array.prototype.slice.call(arguments))
        return self.apply( this instanceof fBound ? this : thatArg, args)
      }
    
      let fNOP = function () {}
    
      if (self.prototype) {
        fNOP.prototype = self.prototype
      }
    
      fBound.prototype = new fNOP()
    
      return fBound
    }
    
    Copy the code

    FNOP and Person use the same prototype, and fbound. prototype is an instance of fNOP, whose __proto__ refers to Person.prototype. Therefore, modifying fbound. prototype directly does not change Person’s Prototype.

At the end

There are many other differences between the above implementation of bind() and the actual algorithm. To paraphrase function.prototype.bind (), though there may be other differences, there is no need to list them further.

One last note

  1. Bind () to a function, and then bind() again has no effect. When fn is finally called, this is always referred to thisArg from the first bind().

  2. After binding this with bind(), you cannot use CAL () and bind() to redirect the function.

reference

  1. Function.prototype.bind()
  2. Bind yourself
  3. The use and implementation of bind() method in Javascript
  4. Javascript basics call, apply, bind
  5. JS call, apply, bind methods in detail