JS Bind

JS Bind, Apply, Call

Bind

We use the Bind () method primarily to call a function with the this value set explicitly.
It occurs when we use the this keyword when a function or method is invoked.

Bind appear in ECMAScript5, so for older systems you can add this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError ('Function.prototype.bind' +
' - what is trying to be bound is not callable');
}

var aArgs = Array.prototype.slice.call (arguments, 1),
fToBind = this,
fNOP = function () {
},
fBound = function () {
return fToBind.apply (this instanceof fNOP &&
oThis ? this : oThis,
aArgs.concat (Array.prototype.slice.call (arguments)));
};

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP ();

return fBound;
};
}

Let’s use an example: when we click the button we populate the text field randomly

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var user = {
data: [
{name: 'T. Woods', age: 37},
{name: 'P. Mickelson', age: 43}
],
clickHandler:function (event) {
// random number between 0 and 1
var randomNum = ((Math.random () * 2 | 0) + 1) - 1;

// This line is adding a random person from the data array to the text field
$ ('input').val (this.data[randomNum].name + " " + this.data[randomNum].age);
}
}

// Assign an eventHandler to the button's click event
$ ('button').click (user.clickHandler);

If we use a framework like JQuery or Backbone.js it will work as it will do most of the binding process for us, but if not this will fail as the method is bound to the HTML element (it is where the method is executed on).

So to bind we change:

1
$ ('button').click (user.clickHandler);

for:

1
$ ('button').click (user.clickHandler.bind (user));

Check also its use for global and local scope:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// This data variable is a global variable
var data = [
{name: 'Sam', age: 12},
{name: 'Alex', age: 14}
];

var user = {
// local data variable
data: [
{name: 'T. Woods', age: 37},
{name: 'P. Mickelson', age: 43}
],
showData: function (event) {
// random number between 0 and 1​
var randomNum = ((Math.random () * 2 | 0) + 1) - 1;
console.log (this.data[randomNum].name + ' ' + this.data[randomNum].age);
}
}

// Assign the showData method of the user object to a variable
var showDataVar = user.showData;
showDataVar (); // Sam 12 (from the global data array, not from the local data array)

// FIX: Bind the showData method to the user object
var showDataVar = user.showData.bind (user);
// Now we get the value from the user object,
// because the 'this' keyword is bound to the user object​
showDataVar (); // P. Mickelson 43

“Apply” and “Call”

Allow us to borrow functions and set the value this in function invocation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// global variable for demonstration
var avgScore = 'global avgScore';

//global function
function avg (arrayOfScores) {
// Add all the scores and return the total
var sumOfScores = arrayOfScores.reduce (function (prev, cur, index, array) {
return prev + cur;
});

// The 'this' keyword here will be bound to the global object,
// unless we set the 'this' with Call or Apply
this.avgScore = sumOfScores / arrayOfScores.length;
}

var gameController = {
scores: [20, 34, 55, 46, 77],
avgScore: null
}

// If we execute the avg function, 'this' inside
// the function is bound to the global window object:
avg (gameController.scores);
// Proof that the avgScore was set on the global window object
console.log (window.avgScore); // 46.4
console.log (gameController.avgScore); // null

// reset the global avgScore
avgScore = 'global avgScore';

// To set the "this" value explicitly, so that 'this' is bound to the gameController,
// We use the call () method:
avg.call (gameController, gameController.scores);

console.log (window.avgScore); //global avgScore
console.log (gameController.avgScore); // 46.4

More info