Bobylito

07 Mar 2014

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 programmatic way
// 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!


Comments