Javascript Function Binding
Grant
Posted on June 29, 2020
Function binding is important because it defines the object on which a function will be called. Consider the example below:
const whereYat = "Outside";
const mainObj = {
whereYat: "Inside",
find: function() {
return whereYat;
}
};
const otherObj = {
whereYat: "Ain’t dere no more."
};
mainObj.find(); // “Outside”
There are three locations where something is named whereYat
: a variable (const
) declared on the global scope, a property of the object named mainObj
, and a property of the object named otherObj
. The function inside mainObj
, when called, will return one of these whereYat
values. As written, it will return Outside
, since the function call is made on the global scope, where the variable whereYat
lives.
Intuitively, the purpose of this code snippet would appear to be different than its current result, but how does one take control of what value function find
returns? The first thing our code needs would be keyword this
:
const whereYat = "Outside";
const mainObj = {
whereYat: "Inside",
find: function() {
return this.whereYat;
}
};
const otherObj = {
whereYat: "Ain’t dere no more."
};
mainObj.find(); // “Inside”
Keyword this
can lead to much confusion, but its use is necessary to prescribe exactly which object a function will operate on. The updated example is called “method invocation”, where this
refers to the object on which the function (method) is called. A helpful tool is to look at what’s to the left of the dot at call time. This code returns the value of whereYat
from inside mainObj
. But what if we wanted the function to return the value of whereYat
inside otherObj
? Passing otherObj
as an argument doesn't work because this
is still bound to the object on which the method is called (the object to the left of the dot). Therefore, mainObj.find(otherObj)
still returns Inside
.
The keywords .call()
, .apply()
, and .bind()
are helpful here, as they allow the function call to be applied to a passed-in object:
const whereYat = "Outside";
const mainObj = {
whereYat: "Inside",
find: function() {
return this.whereYat;
}
};
const otherObj = {
whereYat: "Ain’t dere no more."
};
mainObj.find.call(otherObj); // "Ain’t dere no more."
In the updated function call, the .call()
method links keyword this
to the argument object, otherObj
. Using .apply()
would give the same result here. But what happens if the function call is made with .bind()
?
mainObj.find.bind(otherObj); // returns the actual find [function]
The string at otherObj
's whereYat
key isn't returned because .bind()
doesn't actually call the function like .call()
(and .apply()
). To return the value we're looking for, we would have to call the .bind()
method immediately:
mainObj.find.bind(otherObj)(); // "Ain’t dere no more."
This difference may make .bind()
appear to be less useful. .bind()
, howver, has a special capability .call()
and .apply()
don't have, which is to permanently "bind" the function to the argument object.
const whereYat = "Outside";
const mainObj = {
whereYat: "Inside",
find: function() {
return this.whereYat;
}
};
const otherObj = {
whereYat: "Ain’t dere no more."
};
const bindToOther = mainObj.find.bind(otherObj);
bindToOther(); // "Ain’t dere no more."
By setting the .bind()
method call to a variable (bindToOther
), we've saved the connection between otherObj
and the mainObj.find
method. We can call this configuration anytime we want using the bindToOther
variable name. For example, let's say we use .call()
again to call the function on mainObj
, returning Inside
:
mainObj.find.call(mainObj); // “Inside”
We can go right back to the bindToOther()
variable to once again return the result of calling the find
method on otherObj
:
bindToOther(); // "Ain’t dere no more."
This method of remembering specific function calls is useful not only in reducing code repetition, but also in reducing bugs that result from unintended function binding.
Posted on June 29, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.