Why function bind matters little in AngularJs

AngularJS relies on closures rather than modules, removing need to bind a context.

Functions

JavaScript is powerful in part because functions are first class citizens. They can be passed as arguments to other functions, returned from functions and assigned to variables. The problem is that when passing functions around, the original context is lost. For example:

1
2
3
4
5
6
7
8
9
var foo = {
name: 'foo',
printName: function() {
console.log(this.name);
}
};
foo.printName(); // prints 'foo'
var printer = foo.printName;
printer(); // undefined

We are storing reference to method printName from object foo, but the assignment looses the ownership context. The value of this inside the printName has no meaning when called via printer. In fact, if used exactly like written above, this is bound to window in the browser (or global in Nodejs). If we used 'use strict'; inside printName or globally, trying to call printer() would generate an exception TypeError: Cannot read property 'name' of undefined.

I often see a solution to this problem that uses local variables named that or me and function closures, for example

1
2
3
4
5
6
7
8
9
// inside some object
doSomething: function () {},
init: function () {
var that = this;
$('element').on('someEvent', function () {
// the callback function passed as reference is no longer bound to object
that.doSomething();
});
}

Function.bind

Enter Function.bind, part of EcmaScript 5 and available pretty much everywhere (or through a es5-shim). It creates a new function that binds this to the object. Same example works as expected

1
2
3
4
5
6
7
8
9
10
'use strict';
var foo = {
name: 'foo',
printName: function() {
console.log(this.name);
}
};
foo.printName(); // prints 'foo'
var printer = foo.printName.bind(foo);
printer(); // prints 'foo'

If you find writing object.methodName.bind(object) too verbose, I wrote dotdot Node loader hook that allows shortcut

1
2
3
4
5
6
7
8
9
10
11
'use strict';
require('dotdot');
var foo = {
name: 'foo',
printName: function() {
console.log(this.name);
}
};
foo.printName(); // prints 'foo'
var printer = foo..printName();
printer(); // prints 'foo'

Function.bind for partial application

Aside from preserving the right context, I use function.bind to pass default arguments to the callback function removing temporary or instance variables. For example:

1
2
3
4
var numbers = [1, 2, 3, 4];
function mul(a, b) { return a * b; }
console.log(numbers.map(function (x) { return mul(2, x); }));
// [ 2, 4, 6, 8 ]

Notice how we use a temporary function to keep passing number 2 to the multiplication function. We can use bind to bind both context (in this case undefined), and any number of arguments!

1
2
3
4
5
var numbers = [1, 2, 3, 4];
function mul(a, b) { return a * b; }
var x2 = mul.bind(null, 2); // 1
console.log(numbers.map(x2));
// [ 2, 4, 6, 8 ]

In line // 1 we created and returned a new function that passes 2 as first argument to function mul(a, b). We could have bounded all arguments, and even more than the function expects - the extra arguments are just ignored.

Why you do not see Function.bind used in AngularJs applications

When looking through AngularJs examples, I did not find many uses of Function.bind. Most often people describe data binding, but not function binding. Why is there a difference with other MVC frameworks that even provide their own wrapper for binding an object to callback function?

I think the answer is in clean separation between the controller and the view. All data in AngularJS is supposed to be a property of $scope object. The framework manages to route any ng-click to the correct scope object under the hood, without the developer thinking about this. Inside a called function, this points to the $scope object

1
2
3
<body ng-controller="MainCtrl">
<p ng-click="clickMe()">Click me</p>
</body>

when clicked the following controller function

1
2
3
4
5
6
app.controller('MainCtrl', function($scope) {
$scope.clickMe = function() {
console.log(this === $scope);
};
});
// prints true

Secondary reason for not seeing function.bind used often inside AngularJs controller code: functions that are defined inside the controller function just use $scope object to access the data instead of properties attached to this. Even functions defined inside the link function can directly work with the scope variable.

1
2
3
4
5
6
7
// controller function
link: (scope, element) {
element.on('someEvent', function () {
// don't care what the context is!
scope.eventCount += 1;
});
}

Conclusion

The usefulness of function.bind to remove temporary variables cannot be overstated. At the same time, AngularJs with its dependency injection of $scope object mitigates the most obvious use case for preserving the right context.