Monday, September 23, 2013

How To Use .call(), .apply() and .bind() In Javascript

Back in March I wrote a post "Understanding Constructor Functions in Javascript and This keyword" in which, while explaining the concept of this in Javascript, I stated that it is impossible to tell what the this keyword references by just looking at the function definition. You only get to decide or know what the this keyword refers to, by the way the function is called. Call the function one way, and have this refer to one object, call it another way and you can have it refer to a totally different object. Showing that in Javascript, this is a context-indicator.

I mentioned that normal calling of a function (i.e. have a function fx, call it via fx()) and calling with the new keyword i.e. (new fx()) are some of the various ways to call a Javascript function and have the this keyword switch context.

There are 3 other ways a Javascript function can be called with the ability to determine what this keyword references. call, apply and bind. To be technically specific: Function.prototype.call, Function.prototype.apply, and Function.prototype.bind.


.call()

.call() allows the calling of a function in a way that this can be specified, while also passing in required arguments, if any, to the function. i.e. having a function fx:

var fx = function (arg1,arg2) {
          console.log(this,arg1,arg2);
}

and calling it:

fx.call(null, "first params", "second params");
//logs: null, “first params”, “second params”

//and...

fx.call({id : 1, name : "akpos"}, "first params", "second params");
//logs {id : 1, name : "akpos"}, "first params", "second params"

.apply()

.apply() is very similar to .call(), just that it requires you to specify the passed arguments to the function as arrays. That is, still using the previous fx function defined above. we can call it thus:

fx.apply(null, ["first params", "second params"]);
//logs null, "first params", "second params"

//and...

fx.apply({id : 1, name : "akpos"},[ "first params", "second params"]);
//logs {id : 1, name : "akpos"}, "first params", "second params"

as you can see, .call() and .apply() only differ in how they handle function arguments.

.bind()

.bind() is quite different from .call() and .apply(). It also allows you to determine what the value of this would be, but it does not call the function but returns an exact copy of the function but with the specified this bound appropriately.

Still using the fx function defined, we use .bind() to create a new function fxbound which has the this referencing the value specified in .bind()

var fxbound = fx.bind(null, "first params", "second params");

fxbound();
//logs null, "first params", "second params"

var fxbound = fx.bind({id : 1, name : "akpos"}, "first params", "second params")

fxbound();
//logs {id : 1, name : "akpos"}, "first params", "second params"


.call(), apply(), bind(), is obvious, makes it easier to write generic function which can be called on different objects;

A trivial illustration:

var shoppingCart = (function(){
         var _calculatePrice = function () {
                    return this.price * this.amount;
         };

         return {
                    calculatePrice : _calculatePrice
         }
})();


var goods = {
           name : ‘hammer’,
           price: 199,
           amount : 2
};

shoppingCart.calculatePrice.call(goods);


in the definition of _calculatePrice that is revealed via calculatePrice, you have this, whose value is then made the goods object at the point of calling it.

There are varied scenarios where applying this pattern comes in handy.

I thus like to have a mental model in which I think of a function that has this in its definition as a generic construct I can pass in different objects (with expected properties) in, and have it operated on.

I am writing a book: TypeScript Beyond The Basics. Sign up here to be notified when it is ready.

4 comments:

Unknown said...

Great! Here is another simple example the difference between call() and apply()

Techsin said...

this is the best example on internet that demonstrate the point clear and simple.


It doesn't assume you are a retard, and then doesn't go on and on forever without telling the main thing.

This helped me where SO, and javascriptissexy failed.

Unknown said...

SIMPLY AWESOME EXPLANATION!!!!

Pradeep said...

Good example