Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is `new` operator able to override hard-binding, in the Function.prototype.bind(..)

This is a purely theoretical question. I am learing javascript from 'you don't know js', and i was stuck on the implementation of the bind function in JS. consider the following code:

function foo(something) {
  this.a = something;
}

var obj1 = {};

var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2

var baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3

In the above snippet, we bind foo() to obj1, so the this in foo() belongs to obj1 and that's why obj1.a becomes 2 when we call bar(2). but the new operator is able to take precedence and obj1.a does not change even when bar(3) is called with new.

Below is the polyfill provided by the MDN page for bind(..):

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 &&
                    oThis ? this : oThis
                ),
                aArgs.concat( Array.prototype.slice.call( arguments ) )
            );
        }
    ;

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
};
}

The part that's allowing new overriding according to the book, is:

this instanceof fNOP &&
oThis ? this : oThis

// ... and:

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();

so , now the main point. according to the book: "We won't actually dive into explaining how this trickery works (it's complicated and beyond our scope here), but essentially the utility determines whether or not the hard-bound function has been called with new (resulting in a newly constructed object being its this), and if so, it uses that newly created this rather than the previously specified hard binding for this."

how is the logic in the bind() function allows new operator to override the hard binding?

like image 699
vikrant Avatar asked Oct 02 '17 14:10

vikrant


People also ask

What is function prototype bind?

prototype. bind() The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

How does bind work in JavaScript?

Summary. The bind() method creates a new function, when invoked, has the this sets to a provided value. The bind() method allows an object to borrow a method from another object without making a copy of that method. This is known as function borrowing in JavaScript.

Why do we use the bind method?

We use the Bind() method to call a function with the this value, this keyword refers to the same object which is currently selected . In other words, bind() method allows us to easily set which object will be bound by the this keyword when a function or method is invoked.

What is hard binding in JavaScript?

Hard binding with the built-in utility bind(..) You need to pass an object to bind(object) and it returns a new function that is hardcoded to call the original function with this context set as you want. The new function is permanently bound to the first argument of bind , regardless of how the function is being used.


2 Answers

First, it is important to understand the difference between an object's prototype (represented the spec as [[Prototype]] and accessible via the function Object.getPrototypeOf or the deprecated __proto__ property) and the property on a function whose name is prototype. Every function has a property named prototype which is used when the function is called with new.

When you call a function with new, that function is supplied with a this value set to a newly-constructed object whose prototype (i.e., [[Prototype]]) is set to the prototype property of the function being called. That is, when you call new Foo(), then when the code inside Foo is run, the this value will be an object of the form

{ [[Prototype]]: Foo.prototype }

Let's briefly meet the cast of variables:

  • fToBind is the function being bound: for foo.bind(...), foo is fToBind.
  • fBound is the bound version of fToBind; it is the returned value of the bind operation. fBound acts like a gatekeeper for the original fToBind function, and decides what this value fToBind gets when it is invoked.
  • oThis is the first argument supplied to bind, i.e., the object being bound to the function's this.
  • fNOP is a function whose prototype property is set to fToBind.prototype
  • fBound.prototype = new fNOP() causes these to be true:

    Object.getPrototypeOf(fBound.prototype) === fNOP.prototype
    Object.getPrototypeOf(fBound.prototype) === fToBind.prototype
    

When fBound is called with new, then the this that is supplied to fBound is of the form

{ [[Prototype]]: fBound.prototype }

and fBound.prototype is an object of the form

{ [[Prototype]]: fNOP.prototype }

making the full form of this equivalent to

{ [[Prototype]]: { [[Prototype]]: fNOP.prototype } }

So, fNOP.prototype is in the prototype chain of the newly-created this object when fBound is called with new. That's exactly what the object instanceof constructor operation tests for:

The instanceof operator tests the presence of constructor.prototype in object's prototype chain.

The order of operations between && and ternary here is:

(this instanceof fNOP && oThis) ? this : oThis

If this has fNOP.prototype in its prototype chain and the original bind call was given a truthy first argument to bind to the function, then use the natrually-created this supplied to fBound when it is called with new and supply that to fToBind instead of the bound this.

like image 195
apsillers Avatar answered Sep 25 '22 02:09

apsillers


new takes precedence over a bound this value because that's how the language was defined.

First you have a function. Then you bind a this value, and call it normally. As expected, the value of this is the bound value.

Then you call that same function with new, and your value is overridden. Why? Because calls using new are instructed by the language design, and therefore by the language implementation, to ignore the bound this value, and replace it with the new object being constructed.

A language implementation is just a program. And like any other program, it follows rules. So the rule in this case is that new gets to dictate the value of this irrespective of any bound value.

like image 41
llama Avatar answered Sep 24 '22 02:09

llama