preface

Before reading this article, I hope you know something about function.prototype. bind.

If not, it is highly recommended to check out the MDN’s description of it, airfare.

It has the following two characteristics:

  1. Bind multiple times, with only the first bind passing in this binding taking effect
  2. Using the constructor returned by the new operation bind, the previously bound this is invalidated

Bind the polyfill

MDN gives polyfill of bind for backward compatibility.

if(! Function.prototype.bind) { Function.prototype.bind =function(oThis) {
    if(typeof this ! = ='function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          return fToBind.apply(this instanceof fNOP
                 ? this
                 : oThis,
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    if (this.prototype) {
      // Function.prototype does not have a prototype property
      fNOP.prototype = this.prototype; 
    }
    fBound.prototype = new fNOP();

    return fBound;
  };
}
Copy the code

A sample code

var o1 = { a: 1 }
var o2 = { b: 2 }
var f = function() {console.log(this) console.log([].slice.call(arguments))} var f1 = f.bind(o1, 1, 2) var f2 = f1.bind(o2, 3, arguments) F2 (5, 6) // CCopy the code

There are both forward and reverse learning methods, so let’s explain this polyfill by running code

Analysis of the

The entire process of running this code is then parsed from the execution context stack. If you are not familiar with the “execution context stack”, I recommend reading my other article, Execution Context

1. Initial global execution context:

  1. Variable objects: o1, O2, f, F1, f2
  2. Scope chain: Currently empty
  3. This points to the window

2. The execution context added when line A is executed:

  1. Variable objects: oThis === o1, aArgs === [1, 2], fToBind === F, fNOP, fBound
  2. Scope chain: Global execution context
  3. This refers to f
  4. Return f1, pointing to the variable object’s fBound and its prototype chain: fbound.prototype. proto === f.protoType

3. The execution context added when line B is executed:

  1. Variable objects: oThis === O2, aArgs === [3, 4], fToBind === F1, fNOP, fBound
  2. Scope chain: Global execution context
  3. This points to F1
  4. Return f2, pointing to the variable object’s fBound and its prototype chain: fbound.prototype. proto === f1.prototype

4. The execution context added to the C line:

  1. Variable object: arguments
  2. Scope chain: more complex, see below
  3. This points to the window

Line C actually executes the function twice:

For the first time:

  1. Arguments === [5, 6]
  2. Scope chain: line B execution context (closure), global execution context
  3. This points to the window
f2(5, 6) === return f1.apply(o2, [3, 4, 5, 6])
Copy the code

The second:

  1. Arguments === [3, 4, 5, 6]
  2. Scope chain: Line A execution context (closure), global execution context
  3. This points to o2
return f1.apply(o2, [3, 4, 5, 6])  === return f.apply(o1, [1, 2, 3, 4, 5, 6]
Copy the code

Results of 5.

So the print of F2 (5, 6) is

{a: 1}
[1, 2, 3, 4, 5, 6]
Copy the code

You can run the results directly into Chrome’s developer tools.

The two bright spot

1. Maintain prototype relationships

I’m using the term “prototypical inheritance” for reference to my other article on class correlation.

The purpose here is to preserve the prototype of the original function (f) for the second highlight.

2. Bind does not affect new

I’m sure you’re confused about this code in fBound

this instanceof fNOP ? this : oThis
Copy the code

The purpose of this is so that the function returned by bind does not affect the creation of objects by the new operator (this is ignored).

If the following statement is executed, then modify f on the basis of the door:

var f = function() { this.c = 3 console.log(this) console.log([].slice.call(arguments)) } var f2Obj = new f2(5, 6); // Run the procedure, and the following this refers to the new object to be created: f2(5, 6) ===return f1.apply(this, [3, 4, 5, 6] === returnF.ply (this, [1, 2, 3, 4, 5, 6] // Result (executed on Chrome) prints: f {c: 3} [1, 2, 3, 4, 5, 6] and f2obj.c === 3Copy the code

conclusion

I couldn’t help but sigh that the author of Polyfill is too careful in thinking. I can only understand the whole design idea step by step by parsing the execution context.

  1. Save the arguments passed to bind each time with closures, including thisArg and args
  2. The returned fBound forms a chain of calls, with each fBound referring to the previous one and ending with the original function
  3. The new operator creates new objects without the effect of the previously bound this using primitive inheritance

Thank you for being here.

The original digest of my personal blog, welcome to step in.