Preface:

The bind function has appeared many times in the interview questions I have read in various communities over the past six months, so I am ready to explore it in detail

First let’s look at what MDN says about bind

grammar

fun.bind(thisArg[, arg1[, arg2[, …]]])

parameter

  • ThisArg This parameter is referred to as this when the original function is run when the binding function is called. This parameter is invalid when a binding function is called using the new operator.
  • arg1, arg2, … When the binding function is called, these arguments are passed to the bound method before the arguments.

The return value

Returns a copy of the original function modified with the specified this value and initialization arguments


When code new Foo(…) When executed, the following things happen: 1. A new object inherited from foo. prototype is created. 2. Call the constructor Foo with the specified arguments and bind this to the newly created object. New Foo is the same as new Foo(), where no argument list is specified and Foo is called without any arguments. 3. The object returned by the constructor is the result of the new expression. If the constructor does not explicitly return an object, the object created in Step 1 is used. (Normally, constructors do not return values, but the user can choose to actively return objects to override the normal object creation steps.) If you don’t understand this, you should

function Foo(){} the following code is executednewA simple implementation of Foo()let obj = {};
obj.__proto__  = Foo.prototype
return Foo.call(obj)
Copy the code

For a full implementation of new, check out the god’s blog


implementation

Beggar version, can not be pre-filled parameters, only implementation of the change this point at execution

let obj = {
  ll: 'seve'
};

Function.prototype.bind = function(that) {
  var self = this;
  return function() {
    return self.apply(that, arguments);
  };
};
let func0 = function(a, b, c) {
  console.log(this.ll);
  console.log([a, b, c]);
}.bind(obj, 1.2);

func0(3); // seve
// [3, undefined, undefined
Copy the code

The beggar version is too low, right? So we’ll keep working on it

Es6 premium

Es6 provides structural operators that can be easily used to implement BIND

Function.prototype.bind = function(that, ... argv) {
  if (typeof this! = ='function') {
    throw new TypeError(`The ${this} is not callable`);
  }
  // save the original function
  let self = this;
  // Get the arguments passed in by bind
  return function(. argu) {
    return self.apply(that, [...argv, ...argu]);
  };
};
let func1 = function(a, b, c) {
  console.log(this.ll);
  console.log([a, b, c]);
}.bind(obj, 1.2);

func1(3); // seve
// [1, 2, 3]
Copy the code

Es6 is easy to implement, but when the interviewer tells you that we are running on ES5, you feel happy that bable is a good way to do it, but you should not say that Babel exists, because the interviewer is not likely to ask you how to convert ES6 to ES5. For example, how can the following class array be converted to a real array

Es5 premium

Function.prototype.bind = function() {
  if (typeof this! = ='function') {
    throw new TypeError(`The ${this} is not callable`);
  }
  var self = this;
  var slice = [].slice;
  // Simulate the deconstruction of ES6
  var that = arguments[0];
  var argv = slice.call(arguments.1);
  return function() {
    // Slice.call (arguments, 0) converts an array of classes to an array
    return self.apply(that, argv.concat(slice.call(arguments.0)));
  };
};
let func2 = function(a, b, c) {
  console.log(this.ll);
  console.log([a, b, c]);
}.bind(obj, 1.2);

func2(3); // seve
// [1, 2, 3]
Copy the code

Of course, at this point, this code is a good answer for most interviews, but in order to make a good impression on the interviewer, we need to implement the full bind function in the final version so that we can talk to the interviewer about it

ultimate

So that the function after bind does not lose this when using the new operator. We need to mount the prototype of the pre-bind function onto the prototype of the post-bind function

However, if you change the prototype after bind, it does not affect the prototype before bind. The direct assignment only assigns the address of the Object in the heap, so you need to inherit the prototype to bind instead of assigning it directly. In some places, I have read that Object.crate provides the same effect, if you are interested, but I have tried it myself. When I do new, this is pointing to the wrong position.

The effect of direct assignment

Function.prototype.bind = function(that, ... argv) {
  if (typeof this! = ='function') {
    throw new TypeError(`The ${this} is not callable`);
  }
  // save the original function
  let self = this;
  let func = function() {};
  // Get the arguments passed in by bind
  let bindfunc = function(. arguments) {
    return self.apply(this instanceof func ? this : that, [...argv, ...arguments]);
  };
  // Attach this to the func prototype
  // func.prototype = self.prototype;
  // To avoid func affecting this, use the new operator to copy what is above the prototype
  bindfunc.prototype = self.prototype;

  return bindfunc;
};

function bar() {
  console.log(this.ll);
  console.log([...arguments]);
}
let func3 = bar.bind(null);

func3.prototype.value = 1;

console.log(bar.prototype.value) // 1 You can see that the stereotype after BIND has the same effect on the stereotype before bind
Copy the code

The effect of assignment by inheritance

Function.prototype.bind = function(that, ... argv) {
  if (typeof this! = ='function') {
    throw new TypeError(`The ${this} is not callable`);
  }
  // save the original function
  let self = this;
  let func = function() {};
  // Get the arguments passed in by bind
  let bindfunc = function(. argu) {
    return self.apply(this instanceof func ? this : that, [...argv, ...argu]);
  };
  // Attach this to the func prototype
  func.prototype = self.prototype;
  // To avoid func affecting this, use the new operator to copy what is above the prototype
  bindfunc.prototype = new func();

  return bindfunc;
};

function bar() {
  console.log(this.ll);
  console.log([...arguments]);
}
let func3 = bar.bind(null);

func3.prototype.value = 1;

console.log(bar.prototype.value) // undefined can see that the prototype after bind has no effect on the prototype before bind

func3(5);     // seve
              / / [5]
new func3(5); // undefined
              / / [5]
Copy the code

If the above code or expression is wrong or not precise, please point out, or discuss in the comments section, if you think my article is useful, you can subscribe or star support my blog

In the next series of articles, I plan to write about the implementation of the KOA framework. In the first article, I will take you through the effects and implementation of Object.create