Thursday, October 15, 2015

Function Invocation Patterns in Javascript

In today's blog post, I will be discussing the four different ways in which functions can be invoked in JavaScript.
Other than the declared parameters, every function receives two additional parameters: "this" and "arguments". In object oriented programming, the "this" parameter is very important and we will see how "this" is set in each pattern in this post.


Function Invocation Patterns in JavaScript



Method Invocation

When a function is a property of an object, it is called a "method".  This is the pattern of invoking a function that is part of an object.

var obj = {
    value: 0,
    increment: function(){
            this.value++; 
     }
} 
obj.increment();
console.log(obj.value); 

increment function is part of object obj. The object that is used to invoke the function constitutes "this" in case of method invocation pattern. JavaScript binds "this" at execution time a.k.a. late binding.

Function Invocation

When a function is not a property of an object and is simply invoked using (), that's function invocation pattern.

var sum = add(2,7);

When a function is invoked using this pattern, "this" is associated to the global object. This behavior can lead to confusing errors. Let's take a look at an example:

var name = 'abhi';
var person = {
    name: 'jain',
    printName: function(){
        var helper = function(){
             console.log(this.name)
        }
        helper();   //function invocation pattern           
    }
}

person.printName();         //abhi


Since printName function was defined inside person object, I was expecting the printName function to use the name property from person object. But since printName uses a helper function and the helper function is invoked using function invocation pattern, it associates "this" to global object. As a result, global variable's value is printed.

An object's method cannot use an inner function to help do it's work because the inner function does not have access to the object. The "this" for the inner function is bound to wrong value.
In order to allow inner function to access the object, we can assign the value of "this" to another variable and use that variable in the inner function. Conventionally, that variable is called "that". 

var name = 'abhi';
var person = {
    name: 'jain',
    printName: function(){
        var that = this;
        var helper = function(){
             console.log(that.name)
        }
        helper();   //function invocation pattern          
    }
}
person.printName();    //jain



Constructor Invocation

When we invoke a function using new keyword, it's called constructor invocation pattern.
In this case, a new object is created with a hidden link to the value of the function's prototype member and "this" will be bound to that new object.
JavaScript's prototypal inheritance nature is explained here. JavaScript offers object making syntax similar to classical languages like Java, C# etc with new keyword. This obscures the language's true prototypal nature. 

function Person(type){
  this.type = type;
}
var employee = new Person('employee');

console.log(employee.type);         //employee

Conventionally, the functions to be used with this pattern are kept in variables with capitalized name. 
In this example, the function Person when called with new keyword, returns an object with one property called type. The value of type property is set to the argument passed. 

If a function is intended to be used with this pattern, and somewhere in the code, new keyword is missing, it can lead to difficult to find errors. So we should be careful of the naming convention as that is the only savior.

Apply Invocation

The apply method is defined on every function. We can call the function using this apply method. By using the apply method, we can pass the arguments as array and explicitly choose the value of "this".

var add = function(num1, num2) {
        return num1+num2;
}

array = [5,4];

add.apply(null,array);    //9

So here we passed the arguments as array. 
In the next sample, let's see how to set the "this" object for the function:

var showName = function(){
    console.log(this.name);
}

var obj = {
  name: "Abhi"
}

showName(obj);       //abhi 

If we look inside the add method, we can see the apply and call method:

Call and Apply in function
Similarly, we can use add function using call method as well. Call method is similar to apply method except instead of having arguments as array, we provide list of arguments as seen in the example below:

var add = function(num1, num2) {
        return num1+num2;
}

add.call(null,5,4);      //9

Conclusion

It is important to understand these different patterns because it's very confusing and can lead to hard to find bugs in our JavaScript code. JavaScript has it's own peculiarities and better understanding of these can help us write better code and avoid the bad parts of the language.

Ref: Javascript: The good parts by Douglas Crockford

For future updates to my weekly blog, please subscribe to my blog via the "Subscribe To Weekly Post" feature at the right and follow me on Twitter. Until then Happy Coding :)

1 comment: