Call, Apply and Bind in JavaScript

2014-09-20 17:55 +09:00
They are lots of fun, seriously.
They are lots of fun, seriously.

I’ve once written about the same subject in an article in my former blog, which has been deprecated. I’d like to move it to Medium, but rather than just copying and pasting it, I’d rewrite it. I hope it makes the post easier to read and more fun.

Function

In JavaScript, functions play a important role. As many developers have already known, JavaScript is a functional language. A function in JavaScript is an first-class object which has a function type.

var func = function () {};
typeof func; // => 'function'

call, apply and bind methods are defined in the prototype of functions. It means, the methods can be called in every function.

Object.getPrototypeOf(func).hasOwnProperty('bind')
// => true

Actually, they’re not difficult concepts if you’re skilled enough to write code in JavaScript freely. So, I’m going to focus on some basic knowledge and their common use cases.

What happens when a function’s called

First of all, let’s look into how a function works. When a function is called, usually with parentheses in JavaScript, JavaScript creates a new scope and works as we’ve defined it. However, one hidden variable in the scope which may be unfamiliar to some developers would be receiver.

Receive. Bacon.
Receive. Bacon.

Receiver is an object which receives the function call, or calls the function. We can refer to it with a this variable in funtions.

function bacon() {
  console.log(this);
}
bacon(); // => global object

For normal functions, it’s usually global. However, if we make an object with a new operator, receiver will be an empty object.

new bacon(); // will print {}, return {}

So, we can use a function as an object constructor.

function dog() {
  this.name = 'kitty';
}
var a = new dog();
a.name // => 'kitty'

But what if we forget to use new?

var name = 'doge';
var b = dog();
b // => undefined
name // => 'kitty'

Hazardous. It eventually referred to a global variable, name, and unwantedly changed it. The example above represents the reason why we should use strict mode in JavaScript. With the strict mode, the default receiver will be undefined, not global.

function bacon() {
  'use strict';
  console.log(this);
}
bacon(); // => undefined

The last type of the receiver is an object calling the method.

var obj = {method: function () { console.log(this); }};
obj.method(); // => { method: [Function] }, obj itself

I think that’s it for the receiver. To be honest, there’s more to explain, but it’s pretty enough for this article.

In addition to the receiver, there’s another hidden variable in a function scope. It’s arguments. It’s an array-like object containing every argument passed to the function. Yes, array-like, which means, not an array.

function getArgs() { return arguments; }
var args = getArgs(1, 'Hello', {}, []);
args // => { '0': 1, '1': 'Hello', '2': {}, '3': [] }
args.forEach // => undefined, not an array!

Even though we don’t specify names of parameters in a function definition, we can always access the arguments with arguments in the function scope. For sure, there are some known issues when manipulating it without strict mode, but it’s a little out of scope of this article. You can find more information in many other articles online.

Now, I think we’re ready to look into call, apply and bind methods, as we’ve covered some basic knowledge required to start.

Call

Let me suppose that we had an array-like object. It means it has indexed properties and a length property as well. I might want to use the forEach method of Array, but I can’t do that because it’s not an array and doesn’t have the array prototype.

var arraylike = {0: 'Hello', 1: 'World', 2: 'Bye', length: 3};
arraylike.forEach(console.log);
// TypeError: Object #<Object> has no method 'forEach'

With the call method of the function, we can call the method as if it is an array.

Array.prototype.forEach.call(arraylike, console.log);
[].forEach.call(arraylike, console.log); // alternative

The first parameter of the call method is a receiver. Other parameters would be the parameters of the original function. Now you may be able to understand how the following example works.

function getName() { return this.name; }
getName(); // => undefined
getName.call({name: 'Graham'}); // => 'Graham'

Because we provided a receiver to the function getName, it could refer to the name property.

Apply

Yes, you should apply.
Yes, you should apply.

As I explained about arguments, you can get actual parameters provided in a function with the array-like object. Then what if we’d like to pass an array to a function as its arguments? Let me suggest an example.

function sum() {
  function add (a, b) { return a + b; }
  return [].reduce.call(arguments, add);
}

The function above sums up every argument passed to it. Now you can understand how I called the array’s reduce function with the array-like object, argument, thanks to the call method.

sum(1, 2, 3, 4, 5) // =>15
sum(1, 3, 5, 7) // => 16

It works well with variable-length arguments.

var arr = [1, 3, 5, 7, 9, 11, 13, 15];
sum(arr) // doesn't work

Now, I’d like to pass each element of an array as the function’s arguments. You can use apply to do this.

add.apply(null, arr) // => 64

The first parameter is for the receiver. This time, it’s null as we don’t need it. The second parameter is an array(or array-like) object which should have indexed properties and the length property.

Bind

Yay, finally, bind.

bind is particularly powerful, not only because it can make our code more concise, but because we can make partial functions. ‘Partial function’ means, according to the Wikipedia article, a function which has a subset of domain of the original function(which is usually called, total function). Let me give some examples.

function hi(name) {
  console.log('Hi ' + name + '!');
}

From the simple function above, saying hi to friends, how can we make a function which prints “Hi there!”?

var hiThere = function () { hi('there'); };

It looks just fine, but seems a little bit verbose. Can’t we do this better?

var hiThere = hi.bind(null, 'there');
hiThere(); // => Hi there!

Yes, we just did it in a simpler way. bind literally bound an argument, “there”, and created a partial function which has a subset of the total function’s domain, in this case, an empty set. The first parameter is for a receiver, as same as other function methods. Now I’m pretty sure you could understand an example below.

var calc = {
  add: function (a, b) { return a + b; },
  sum: function () { return [].reduce.call(arguments, this.add); }
};

As we’ve already known, calc.sum will sum up every argument passed to the method. In the method, this refers to the receiver, calc itself.

var func = calc.sum.bind(calc, 1, 2, 3);

I defined a new function func. This time, the receiver calc should be specified as an argument. Otherwise, the method can’t find this.add in its receiver and it’ll fail. Also arguments 1, 2 and 3 are bound, which means the new function is a partial function of calc.sum having them as default arguments. How it works is shown below.

func() // => 6 == (1 + 2 + 3)
func(4, 5) // => 15 == (1 + 2 + 3) + 4 + 5

Pretty simple.

However, you should be careful using bind as it is not supported in IE<9.

Absolutely not this one.
Absolutely not this one.

I want more!

I’m afraid that’s it. However, there are further readings for you! Please read documentations on MDN as well. The links are below. They’re just brilliant.

read other posts