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?
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.
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.
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.
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.
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
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With