1. Implementation of Call

  • Context is optional. If not, the default context is Window
  • To avoid conflicts between function names and attributes of the context, use the Symbol type as the unique value
  • Because Call can pass multiple arguments as arguments to the calling function, you need to strip the arguments out
  • Finally execute and delete CTX [func]
Function.prototype.myCall = function(context) {
    // Check whether the caller is a function. This refers to the function that calls myCall
    if(typeof this! = ='function') {
        throw new Error('caller must be a function')}// 1. If the first parameter is null or undefined, this points to the global object window;
    // 2. The this argument can be passed as primitive data. Native Call is automatically converted with Object()
    const ctx = Object(context) || window; 
    
    // Create a unique Symbol variable to avoid duplication
    const func = Symbol(a); ctx[func] =this; // Set the function as an object property
    
    const args = [...arguments].slice(1);
    
    constres = ctx[func](... args);delete ctx[func];
    
    return res;
}
Copy the code

2. Implementation of Apply

Function.prototype.myApply = function (context) {
  // Determine if the caller is a function. This refers to the function that calls myApply
  if(typeof this! = ='function') {
    throw new Error('caller must be a function')}const ctx = Object(context) || window;
  const func = Symbol(a); ctx[func] =this;

  let res;
  // Check whether any arguments are passed in
  if(arguments[1]) { res =ctx[func](... arguments[1])}else {
    res = ctx[func]
  }

  delete ctx[func];
  return res;
}
Copy the code

3. Implementation of BIND

Step by step, we implement the following four features of BIND:

  • You can specify this
  • Return a function
  • You can pass in parameters
  • Currie,

The simulation implements the first step

Function.prototype.myBind = function(context) {
    const self = this; // this points to the caller
    return function() { // Implement point 2
        returnThe self. The apply (context);// Implement point 1}}Copy the code

The simulation implements the second step

Function.prototype.myBind = function(context) {
    const self = this; // this points to the caller
    
    // Implement point 3, since the first argument is this, only the arguments after the first one are extracted
    const args = [...arguments].slice(1);
    
    return function() { // Implement point 2
        // Arguments refer to the arguments that bind returns
        const bindArgs = [...arguments];
        returnThe self. The apply (context, args. Concat (bindArgs));// Implement point 1}}Copy the code

The simulation implements step 3

That’s most of it now, but there’s one more twist. Bind has the following features:

A binding function can also use the new operator to create objects: this behavior is like treating the original function as a constructor, where the supplied this value is ignored and the parameters of the call are supplied to the mock function.

It’s too abstract. Here’s an example:

var value = 2;
var foo = {
    value: 1
};
function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}
bar.prototype.friend = 'kevin';

var bindFoo = bar.bind(foo, 'Jack');
var obj = new bindFoo(20);
// undefined
// Jack
/ / 20

obj.habit;
// shopping

obj.friend;
// kevin
Copy the code

In the example above, the result of running this.value is undefined, which is neither a global value nor a value in foo, indicating that bind’s this object is invalid, and the implementation of new generates a new object, in which case this points to obj.

This can be done by modifying the prototype of the return function as follows:

/ / the third edition
Function.prototype.bind2 = function (context) {
    const self = this;
    
    const args = [...arguments].slice(1);
    
    const fBound = function() {
        const bindArgs = [...arguments];
        
        / / comment 1
        return self.apply(this instanceof fBound? this : context, args.concat(bindArgs
        ));
    }
    
    / / comment 2
    fBound.prototype = this.prototype;
    return fBound;
}
Copy the code

Note 1:

  • When used as a constructor, this refers to the instance, and the result of this instance of fBound is true, 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 the binding function’s this refers to the context

Note 2.

Change the prototype of the return function to the prototype of the binding function. The instance can inherit the value in the prototype of the binding function, i.e. in the above example, obj can obtain the friend on the bar prototype.

The simulation realizes the fourth step

Prototype = this.prototype; fbound. prototype = this.prototype;

Let’s test it out:

// Test case
var value = 2;
var foo = {
    value: 1
};
function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}
bar.prototype.friend = 'kevin';

var bindFoo = bar.bind2(foo, 'Jack'); // bind2
var obj = new bindFoo(20); // Return correct
// undefined
// Jack
/ / 20

obj.habit; // Return correct
// shopping

obj.friend; // Return correct
// kevin

obj.__proto__.friend = "Kitty"; // Modify the prototype

bar.prototype.friend; // Return error, this has been modified
// Kitty
Copy the code

The solution is to use an empty object as a mediator and assign fbound. prototype to an instance of the empty object (the original type inherits).

const fNOP = function() {}; // Create an empty object
fNOP.prototype = this.prototype; // The empty object's prototype points to the binding function's prototype
fBound.prototype = new fNOP(); // An instance of the empty object is assigned to fbound.prototypeYou can use ES5 directly hereObjectThe.create() method generates a new object, fbound.prototype =Object.create(this.prototype)
Copy the code

Version 4 is now ok with the following code:

Function.prototype.myBind = function(context) {
    const self = this;
    const fNOP = function () {};
    
    const args = [...arguments].slice(1);
    
    const fBound = function() {
        const bindArgs = [...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 Implementation Step 5 (final version)

That’s pretty much it, but the problem is that bind is not called by a function, so you need to throw an exception.

Function.prototype.myBind = function(context) {
    if(typeof this! = ='function') {
        throw new Error('caller must be a function! ')}const self = this;
    const fNOP = function () {};
    
    const args = [...arguments].slice(1);
    
    const fBound = function() {
        const bindArgs = [...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

Reference: github.com/yygmind/blo…