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?
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 ofconstructor.prototype
inobject
'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
.