16

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?

vikrant
  • 2,169
  • 1
  • 21
  • 27
  • You may find [this answer](https://stackoverflow.com/questions/367768/how-to-detect-if-a-function-is-called-as-constructor) useful. – Mitya Oct 02 '17 at 15:05
  • Languages have compile-time and runtime semantics that are defined and implemented. When a JS program is parsed and compiled, calls with the `new` operator are found. This operator is known to be used to call a function with the intent of constructing a new object. As such, the specific algorithm(s) defined by the spec for this circumstance are followed. Part of that, like any call, is setting `this` to the proper value. There's no real magic. The language implementation is just following rules. – llama Oct 02 '17 at 15:13
  • Also, keep in mind that polyfills are "best effort" implementations written purely in JS. They have no access to the language implementation's internals, and so are sometimes unable to perfectly represent the requirements of the spec. – llama Oct 02 '17 at 15:14
  • Here is a simple example `function Foo(x) { console.log(this instanceof Foo); }` -> `Foo(4)` = false `new Foo(4)` = true. – Keith Oct 02 '17 at 15:14
  • @Utkanos that was really helpful but still didn't explain how is `new` able to take precedence over hard binding – vikrant Oct 02 '17 at 15:17
  • Are you asking how native `bind` is specified to work in the ECMAScript spec, or how and why the behavior of `this instanceof fNOP && oThis` polyfill works? – apsillers Oct 02 '17 at 15:28
  • yes mainly how the logic of the polyfil allowing `new` to take precedence – vikrant Oct 02 '17 at 15:29
  • If you're just asking how the polyfill works, you need to clarify that, because your question looks more like you're asking about the relationship of the `new` operator to functions with a bound `this` value. – llama Oct 02 '17 at 15:30
  • i have edited the last part to make it more clear – vikrant Oct 02 '17 at 15:33
  • That really didn't make it any more clear. Your question is still asking how *the `new` operator* is able to override hard-binding, but then you now seem to be saying that you want to know how a specific `.bind()` polyfill method is able to *replicate* the native behavior of `new`. – llama Oct 02 '17 at 15:48
  • "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." and so the new operator able to override hard binding, due to the logic in polyfill. i have mentioned clearly how is the logic in the polyfill determining that weather the function is called with new, if so then new will take precedence. how is the logic in the code working so that new will override hard binding? – vikrant Oct 02 '17 at 15:53
  • See, again you're asking about how `new`, which is an operator, does an override, but then you say it's about the polyfill, which doesn't determine how `new` works, but rather includes logic to emulate how the bound function should behave when invoked with new. That's why your question is unclear. On top of that, you talk about how JavaScript's `.bind()` is implemented but you don't make it clear when you're talking about the native implementation and when you're talking about the polyfill. – llama Oct 02 '17 at 17:26
  • That said, I've answered both questions below. – llama Oct 02 '17 at 17:27
  • possible duplicate of [MDN Function.prototype.bind bound function called as constructor](https://stackoverflow.com/q/23693282/1048572) – Bergi Oct 02 '17 at 18:20

3 Answers3

8

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.

apsillers
  • 112,806
  • 17
  • 235
  • 239
  • 1
    This: `fBound.prototype.__proto__ === fNOP.prototype` and this: `fBound.prototype.__proto__ === fToBind.prototype` can't both be true. Maybe for the second one you meant `fNOP.prototype === fToBind.prototype`? – llama Oct 02 '17 at 17:33
  • "*represented in code as `__proto__`*" - please don't use that anymore, anywhere - it's deprecated and needs to die! Use `Object.create`/`Object.getPrototypeOf`/`Object.setPrototypeOf` instead. – Bergi Oct 02 '17 at 18:23
  • @Bergi I don't disagree! Would you rather I express an object's prototype using `[[Prototype]]` internal slot notation in this answer? Or are you simply cautioning against using it in actual code (a position I wholeheartedly endorse)? – apsillers Oct 02 '17 at 18:26
  • @apsillers Both, I think. – Bergi Oct 02 '17 at 18:28
  • @Bergi extermination complete `:)` – apsillers Oct 02 '17 at 18:40
  • so as i understand the object's prototype is still represented in the code as `__proto__`, and in the spec as `[[Prototype]]`. it is important to note this, because almost all the tutorials on internet use both these terms which confuses the beginners, and this answer has cleared a lot of things at the same time – vikrant Oct 03 '17 at 06:42
2

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.

llama
  • 2,535
  • 12
  • 11
  • I can see you have posted two answers (is that possible?) you can merge the other answer with this – vikrant Oct 02 '17 at 16:27
  • Yes you can have multiple anwsers, and I'm effectively answering two very different questions, depending on how the post is interpreted, so I prefer to keep them separate. – llama Oct 02 '17 at 17:22
1

I guess you're just asking about how they got the polyfill to work.

In that polyfill, fNOP is a no-op function (does nothing when called) that is used simply to have its .prototype inserted into the prototype chain of the returned fBound function. That's done here:

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

So then when the fBound function (the one that gets returned to the caller of .bind()) is invoked, the instanceof operator can check the this value inside the fBound function to see if that value is an instance of fNOP. And if it is, it infers that new was used.

This works (sort of) because instanceof will start at the object it's given on its left hand side, and search the prototype chain to see if any of them are the same object as the .prototype object of the function on its right side. So if new was invoked, the this value will be a new object that has fNOP.prototype in its prototype chain because of the setup that was performed as shown above.

However, this isn't a perfect means of testing. For example, the .call() method could have been used to set the this value of the call to some other instance of the fBound function. So it will look like new was used even when it wasn't, and as such, the bound value for this will not be used as the this value of the call to the original function.

llama
  • 2,535
  • 12
  • 11