Function.prototype.bind

JavaScript polyfill, by , Sunday, April 6th, 2014

The JavaScript Function.prototype.bind method is not easy to write a 100% compliant polyfill for. The polyfill that I am providing here will cover most cases you might encounter, but just be aware, that there are edge cases that fail. After looking at the polyfill it self, we will have a short look at how to use bind, and see when the polyfill can fail.

A useful Function.prototyp.bind polyfill

As I started out saying, this polyfill will cover most use cases of the bind method, and will therefore be good enough in most situations, so lets look at the code;

  1. if (!Function.prototype.bind) {
  2. Function.prototype.bind = function (o) {
  3. var target = this,
  4. args = Array.prototype.slice.call(arguments, 1),
  5. bound = function () {
  6. var F = (function() {
  7. var Fn = function() {};
  8. Fn.prototype = target.prototype;
  9. return Fn;
  10. }()),
  11. self = new F(),
  12. targetSelf = target.apply((function() {
  13. return (this instanceof bound && self) ? self : o;
  14. }()), args.concat(Array.prototype.slice.call(arguments)));
  15. return targetSelf || self;
  16. };
  17. return bound;
  18. };
  19. }

The way this polyfill works is, that we first at line 1-2 first check if the Function.prototype.bind method is available, and if not, we declare it, and initialize it to be our bind polyfill function. At line 3 we save the this value, which is the function on which this method is called. On line 4 we convert all the arguments into an array, except for the first. The reason for this is, that the first argument is the this value to which we want to bind our function, while the other arguments can be preset values of the bounded function. At line 5-17 we initialize the bound function which will be returned as the Function.prototype.bind method. Inside this function, we at line 5-10 create the F function constructor, which has the same prototype as the target function. Then we at line 11 initialize the self variable which is the result of calling the F constructor with the new operator. From line 12-14 we call the apply method in the target function and determine the this value by checking if it is an instance of bound and that self exists. If so self becomes the this value, else o, the provided this value, is returned. The second parameter to apply is of course the concatenated arguments. Then finally at line 16 we check if targetSelf is a truthy (you can read more about truthy values in my article Primitive types, and truthy and falsy values) value, if so we return it, else we return self.

Common uses of the bind method, that works

The most common use of binding is to bind a function to a certain this value, such that no matter where it is executed, the this reference will not be lost. One such example is to define a method in an object, that refers to some value inside the object itself via the this value. If you were to pull out the method from the object, that reference would be lost as the this value now will refer to the one present outside the object. With the Function.prototype.bind method, you can bind a specific this value to a method, which ensures that the the reference is not lost. Lets have a look at an example of this;

  1. var fruitBasket = {
  2. fruits : ['durian', 'dragon fruit', 'apple', 'banana', 'pear'],
  3. getFruit : function() {
  4. return this.fruits[Math.floor(Math.random() * this.fruits.length)];
  5. }
  6. };
  7. // This will return a random fruit from the 'fruits' property array.
  8. console.log(fruitBasket.getFruit());
  9.  
  10. var randomFruit = fruitBasket.getFruit;
  11. // The browser will throw a TypeError.
  12. console.log(randomFruit());

What is happening here is, that we first declare the fruitBasket variable and initialize it to be an object with two properties; fruits which is an array of names of fruit, and getFruit which is a method that returns a random fruit from fruits, referring to it using the this value. Then at line 8 we console log the result of executing the fruitBasket.getFruit method, which will result in a random fruit name being returned and printed to the browser console. This all works, as we are executing the getFruit method in the context of the fruitBasket object, which contains the referenced fruits variable. However, if we naively do as in line 10, assigning the fruitBasket.getFruit method to a variable outside the fruitBasket object, and then (at line 12) execute the function the browser will throw a TypeError. The reason for this, is that the this value that now is referenced is the this of the global window scope, and since it does not have any fruits variable declared, we run into problems. (the TypeError is caused when we try to take the length of the fruits array. The reference this.fruits will be undefined, and trying to take the length of undefined causes a TypeError)

The solution to this problem is of course binding. So if we remove the execution of the randomFruit function at line 12 in the above code, and replace it with the following code, we get what we expect.

  1. var randomFruitBound = randomFruit.bind(fruitBasket);
  2. // Print a random fruit name in the console.
  3. console.log(randomFruitBound());

We have now bound the function to the this value of the fruitBasket object, so when we execute the function, we get a random fruit name returned. This is probably the most common use of binding and it will work just fine with the above polyfill.

Situations where the polyfill fails

As I started out mentioning, this polyfill is not perfect in substituting the build in Function.prototyp.bind method, and now we are going to see in which situations this is.

  1. function Point(x, y) {
  2. this.x = x;
  3. this.y = y;
  4. };
  5. // newPoint is now an object Point {x: 4, y: 2}
  6. var newPoint = new Point(4, 2);
  7. // Prints out 'true'
  8. console.log(newPoint instanceof Point);
  9.  
  10. var FixedXPoint = Point.bind(null, 0),
  11. orionPoint = new FixedXPoint(0);
  12. // Native bind: 'true'
  13. // Polyfill bind: 'true'
  14. console.log(orionPoint instanceof Point);
  15. // Native bind: 'true'
  16. // Polyfill bind: 'false'
  17. console.log(orionPoint instanceof FixedXPoint);

As this code demonstrates, with the polyfill we can no longer tell that the orionPoing in-fact is an instance of both the Point constructor and the FixedXPoint. If this is a situation that occurs in in your code base, this polyfill will not help you, so you would have to come up with something else. However this is an edge case, and most likely not the best way to do things in production code anyway, so as I started out stating, this polyfill will help you in most cases.