Shaun Xu

The Sheep-Pen of the Shaun


News

logo

Shaun, the author of this blog is a semi-geek, clumsy developer, passionate speaker and incapable architect with about 10 years experience in .NET. He hopes to prove that software development is art rather than manufacturing. He's into cloud computing platform and technologies (Windows Azure, Aliyun) as well as WCF and ASP.NET MVC. Recently he's falling in love with JavaScript and Node.js.

Currently Shaun is working at IGT Technology Development (Beijing) Co., Ltd. as the architect responsible for product framework design and development.

MVP


There are three functions under JavaScript Function object which are bind, call and apply. They are very useful when we need to pass the function as a variant but very confused. I'd like to try to explain them here with a sample code in Node.js environment.

 

Pass Function as a Variant

Assume we have a class named "Person" as below. The constructor accepts two parameters for the person's first name and last name. We also added a method named "say", which accepts one parameter then return a string with the person's full name and the parameter value. I also override the "toString" method so that it will return "[object Person]".

   1: var Person = function (firstName, lastName) {
   2:     this._firstName = firstName;
   3:     this._lastName = lastName;
   4: };
   5:  
   6: Person.prototype.say = function (message) {
   7:     return '[' + this + ']: ' + this._firstName + ' ' + this._lastName + ' said: "' + message + '"';
   8: };
   9:  
  10: Person.prototype.toString = function () {
  11:     return '[object Person]';
  12: };

Then I initialized this class and invoke "say" as below.

   1: var shaunxu = new Person('Shaun', 'Xu');
   2:  
   3: console.log("shaunxu.say('Hello world')");
   4: console.log(shaunxu.say('Hello world'));
   5: console.log();

Let's execute it and as you can see the function was invoked without any problems.

image

Now let's do something different. As you should know we can pass a function as a variant, just like string, number and Boolean, etc. So I will retrieve "say" function by its name (which is "say") and set into a local variant, then invoke this variant.

   1: var func = shaunxu['say'];
   2: console.log("func('Hello world')");
   3: console.log(func('Hello world'));
   4: console.log();

The executing result was unexpected. Even I retrieved this function from the instance of my class "Person", when invoked you will find "_firstName" and "_lastName" were all undefined, and "this" was not my "Person" instance, instead it's "[object global]".

image

 

Function Context

The reason why we met the problem above is something I'd like call "Function Context". In JavaScript a function will be invoked with a certain context. When we invoke through "shaunxu.say()", "shaunxu" is the context of the function "say" with the local variants "_firstName" and "_lastName" populated.

But when we linked this function to a local variant, the context of the original function was lost. So that when we invoked this variant, current environment, which is [object global] will be the context. But there is no "_firstName" and "_lastName" in this context. This is the reason why the function returned incorrect result.

image

We can added "_firstName" and "_lastName" as the local variants and executed this function again and we will found it worked but was not used the name defined inside our class.

   1: _firstName = 'Kun';
   2: _lastName = 'Zhang';
   3:  
   4: var func = shaunxu['say'];
   5: console.log("func('Hello world')");
   6: console.log(func('Hello world'));
   7: console.log();

image

We assigned the value to the variants "_firstName" and "_lastName" without defined so that they will became members under the global context. The code only worked under the non-strict mode.

 

Specify Context through Call() and Apply()

Since the problem is that we lost the context of this function when invoked, we can use "call" and "apply" method of JavaScript Function to specify the right context then invoke. The first argument of "call" and "apply" is the context object, in this case it's our "shaunxu" variant. The following arguments are the calling parameters. The only different between "call" and "apply" is, you can pass parameters for following arguments to "call", while you can pass parameters as an array to "apply".

Let's change our code to invoke "say" through "call" and "apply".

   1: var func = shaunxu['say'];
   2:  
   3: console.log("func.call(shaunxu, 'Hello world')");
   4: console.log(func.call(shaunxu, 'Hello world'));
   5: console.log();
   6:  
   7: console.log("func.apply(shaunxu, ['Hello world'])");
   8: console.log(func.apply(shaunxu, ['Hello world']));
   9: console.log();

Then execute our application we will see the function was invoked under our instance even though from the local variant.

image

 

Set Context through Bind()

With "bind()" method, we can set the context of a function. So in the future we can invoke this function variant without specifying the context when invoked.

In the code below I retrieved the function from its key ("say") and invoked the "bind" method to set "shaunxu" object as its context. Then invoke this function variant directly.

   1: var func = shaunxu['say'].bind(shaunxu);
   2:  
   3: console.log("func.bind(); func('Hello world')");
   4: console.log(func('Hello world'));
   5: console.log();

And we can see it returned correctly.

image

By using "bind" we can set any object as the context of a function. This gives us extremely flexibility to extend our code. In the sample code below I bind my function to a new object with "_firstName" and "_lastName" members and it worked as we expected.

   1: var func = shaunxu['say'].bind({ _firstName: 'Ziyan', _lastName: 'Xu' });
   2:  
   3: console.log("func.bind(); func('Hello world')");
   4: console.log(func('Hello world'));
   5: console.log();

image

 

Summary

JavaScript is a very flexible language. Different from C#, we can assign a function to a variant and dealing with it later in JavaScript. But when invoke a function variant, we must pay attention to its context.

We must understand once we assign a function into a variant the context of this function will be changed. It will be the calling function if our code was in a function, or global of it's in top level. When invoke this function variant we should specify correct context through "call" and "apply" method. Alternatively we should set the certain context through "bind".

 

Hope this helps,

Shaun

All documents and related graphics, codes are provided "AS IS" without warranty of any kind.
Copyright © Shaun Ziyan Xu. This work is licensed under the Creative Commons License.

Comments

Gravatar # re: bind(), call() and apply() in JavaScript Function
Posted by Shrikster on 12/17/2013 4:23 AM
Thanks , finally simple short and clear explaining on
call apply bind and function context (scope)
Gravatar # re: bind(), call() and apply() in JavaScript Function
Posted by kenoy on 3/21/2014 5:07 AM
Excellent resource , understood , abt call and apply , easily.
thnx a lot
Post A Comment
Title:
Name:
Email:
Comment:
Verification: