Using bind() in Javascript
This article is an attempt to give further explanations on
Function.prototype.bind,
first explaining which problems it solves and then showing how it could help
design
software.
Bind
Bind is a function of the Function prototype which has the following
signature : Function.prototype.bind( newThis, [arg1, arg2, ...])
Let’s put aside the optionnal parameters for now. When this method is applied on
a
function it creates a new function on which the this object context is
definitely set.
Here is a small sample you can execute in your browser:
//in a web environment
function a() { console.log(this) }
a(); // prints out the window
var b = a.bind({ foo : “bar”});
b(); // prints out the object { foo : “bar”}
When the function a is called, it uses the default environment object
which is window
. bind
call creates a new function and sets the this
of the function to some random object here. b call prints out this object.
As I stated earlier it has also this interesting side effect of setting this
for good.
// We continue on the previous environment
a.call({baz : “bar”}); // prints out {baz : “bar”}
b.call({baz : “bar”}); // prints out {foo : “bar”}
call
runs the function it is applied on with this
set to the first
parameter
given to it. Given that a and b are basically the same functions, one could
have
expected to receives the same output. And this is not the case here. Bind
is
behind
the scene changing the value of this
dynamically before the function is
actually
called.
There is a case I encountered in which I needed bind
. But it wasn’t obvious to
me
at the time. I wanted to be able to use console.log
directly as a callback.
But I
couldn’t because the browser was throwing some ‘illegal invocation’ exception.
console.log(”toto”); //Prints french foo bar toto ;)
// Let’s say we want to inspect some event objects in a programmaticway
// You could try this
document.body.addEventListener(”click”, console.log);
// but it won’t work, throwing a dreadful ‘illegal
// invocation’ Exception
// Another approach
document.body.addEventListener(”click”, function consoleLogProxy(){
console.log(arguments);
});
// it doesn’t report a correct line number
// The solution :
var log = console.log.bind(console)
document.body.addEventListener(”click”, log);
In the first attempt, an illegal invocation exception was throwned because
log
wasn’t executed with the console
context.
The second example is a classic way to do the job. But in this particular
case,
it has a problem, the browser console won’t report the correct line number. If
this function is a util function of your framework in this fashion, the line
number
reported by the console will always be the one where the console.log is called.
And
this is not very very useful.
Beyond this
bind
takes a variable number of arguments. Each of these other
arguments are set the same way this
is set for the future calls
on the newly created function.
var logHello = console.log.bind( console, “Hello”);
logHello( “Peter” ); // output \> “Hello” “Peter”
Although the first argument may solve some headaches related to this
, the
other
arguments allow a whole bunch of software design tricks. One may use functions
as a
way to create high level behaviours and then use bind to fill in the gaps.
This is the killer feature lying into bind.
Designing with functions
The classical example for such design is addOne. Let’s consider the function
add
, which takes two numbers and return the sum of it. AddOne
is the
function that takes one number and return the sum of this number and one. This
is how it looks :
var add = function add(n, m) {
return n + m;
};
var addOne = add.bind(null, 1);
addOne(2); // 3
To be honest, this example always left me with the feeling it was useless. If
you
feel me, here is another one I believe is better. It is about making stuff, be
it
a tasty cake or a cool machine. Here is how it all start :
var makeLemonPie = function () {
var ingredients = getIngredients();
var lemonPie = lemonPieRecipe(ingredients);
return lemonPie;
};
var makeRobots = function () {
var parts = getParts();
var robot = robotPlans(parts);
return robot;
};
The first part of designing is to look for similarities. We have two functions
that create stuff. First they fetch some ingredients and then use a recipe
function
to combine them and then return the resulting stuff.
The common pattern can therefore be extracted into a more generic function :
var make = function (formula, getIngredients) {
var ingredients = getIngredients();
var product = formula(ingredients);
return product;
};
And then it can used to create robots and lemon pies.
var aRobot = make(robotPlans, getParts);
var aLemonPie = make(lemonPieRecipe, getIngredients);
As long as we provide the recipe and some resource provider, everything
works fine. But if we were to share this code with a third party, they
would have to know about everything, the formula/recipes and the ingredients
provider. Instead, we can use bind
to provide a proper API.
// Maker module
var robotM = require("./robot.js");
var cakeM = require("./lie.js");
var make = function (formula, getIngredients) {
var ingredients = getIngredients();
var product = formula(ingredients);
return product;
};
module.exports = {
makeRobot: make.bind(null, robotM.robotPlans, robotM.getParts),
makeLemonPie: make.bind(null, cakeM.recipe, cakeM.getIngredients),
abstractMake: make,
};
The make
function is a general one. It describes a process, something that
is, by nature, abstract. It is a very high level vision of the making/baking
process. It doesn’t hold any assumptions on objects that will be built in the
future. There are no constraints on which ingredients are required nor on the
output.
People call this form of specialization of functions currying or partial
application. And as you can see, it is a poweful way to structure code in a
functionnal style.
Bound callbacks
I want to finish this post with a way to rewrite a very common pattern. When
passing a callback function which is already, we may want some parameters to
be
already passed. The common practice is to define an anonymous function. But it
gets a bit abstract.
Let’s consider the following function :
function a(n) {
console.log(Data.now(), n);
}
This function prints the execution time and the value passed in the first
argument. The date is not relevant for now.
Let’s take setTimeout
as the callbacked API for the next example on how would
be the
common practice. It is not really a very real life example but most of us
are familiar with its use. Here it is :
a(0);
setTimeout(function () {
a(5);
}, 5000);
The first call to a prints the time followed by 0. The second call, after about
5 seconds,
prints the time (with 5 or more seconds more than the previous call), followed
by 5.
We had to create a new function just to be able to preset one value, this is a
bit verbose.
And in this kind of states we might be tempted to try shortcuts. Such as :
a(0);
setTimeout(a(5), 5000);
The problem here is that the second invocation is evaluated right away, not
after 5 seconds.
This is shown by the inside call to Date.now()
that is logged to the console,
not 5 seconds
but milliseconds after. It is very much like we did this :
a(0);
a(5);
Hopefuly we have bind
to help for doing correct and elegant code :
a(0);
setTimeout(a.bind(null, 5), 5000);
The code in this case is strictly equivalent to the first solution. But beware,
you have to
keep in mind that bind
will create a new function which holds reference to
parameters. This
might lead to memory leak.
Conclusion
Bind is powerful, elegant, leads to a whole new world of program design and
solve some problem
with callbacks. Use it, this is good!