Invocation Patterns in JavaScript

There are four different invocation patterns in JavaScript that determine what the hidden parameter this is bound to.1

Method Invocation Pattern

A method is a function tied to a property on an object. For methods, this is bound to the object upon invocation. For example:

var person = {
    name: 'Calvin',
    age: 25,
    greet: function () {
        alert('My name is ' + this.name + '.');
    }
};
person.greet(); //My name is Calvin.

In this example, this is bound to the person object upon invoking greet because greet is a method of person.

Function Invocation Pattern

For functions that are not properties on objects, this is bound to the global object. This is not very intuitive and is often considered one of the ‘bad parts’ of JavaScript–a mistake in language design. Naturally, you’d think this would be bound to the parent function, and that would have been much more helpful. Regardless, most developers overcome this problem by assigning this to a variable in the parent function. For example:

// Add a new method to person
person.calculateAge = function (yearsFromNow) {
    var self = this;

    function yearsOld() {
        return self.age + yearsFromNow;
    }

    alert('I will be ' + yearsOld() + ' years old ' + yearsFromNow + ' years from now.');
}
person.calculateAge(10); //I will be 35 years old 10 years from now.

In this example, I maintain a reference to the context of this by assigning it to the variable self. At the time of assignment, this is bound to the person object. As a result, I can access the property age on the person object from within the yearsOld function. self and that are common names for variables that maintain the context of this.

What if I had used this instead of self?

person.calculateAgeWrong = function (yearsFromNow) {
    function yearsOld() {
        return this.age + yearsFromNow; //NaN
    }

    alert('I will be ' + yearsOld() + ' years old ' + yearsFromNow + ' years from now.');
}
person.calculateAgeWrong(10);

Constructor Invocation Pattern

In JavaScript, functions can be invoked with the new prefix similar to the way objects are constructed in other languages. When this happens, this is bound to the new object. In addition, the resulting object is created with a link to the hidden prototype property of the function. This is what makes JavaScript a prototypal inheritance language as opposed to a classical inheritance language. There are no classes, but objects can inherit properties from other objects.

Functions that are designed to be called with the new prefix are by definition constructors. These functions are distinguished from others by using PascalCase as opposed to camelCase.

var Person = function (name) {
    this.name = name;
};

Person.prototype.greet = function () {
    return this.name + ' says hi.';
};

alert(new Person('Calvin').greet()); //Calvin says hi.

Notice the greet function uses this to access the name property. this is bound to Person.

Apply Invocation Pattern

As a functional object-oriented language, JavaScript makes it possible for functions to have methods as well. The apply function is a method on the Function.prototype–the prototype for all JS functions. apply makes it possible to use one object’s method in the context of another. We can do so by supplying an array with the correct number arguments and and the object to which this will be bound, also known as the context. Therefore, apply can take two arguments: (1) a context for this and (2) an array of arguments that will be applied to the method at hand.

var calvin = new Person('Calvin');
var hobbes = {name: 'Hobbes'};
alert(calvin.greet.apply(hobbes)); //Hobbes says hi.

Even though hobbes does not have a greet method, we can still apply the greet method from calvin because hobbes has a name property. If hobbes didn’t have a name property, the invocation would fail. This example demonstrates the invocation of the apply function with only one argument. greet doesn’t have any parameters so we didn’t provide apply an array of arguments.

Let’s say we have a method greetFriends that takes two arguments–two objects with a name property.

Person.prototype.greetFriends = function (friendA, friendB) {
    return this.name + ' says hi to ' + friendA.name + ' and ' + friendB.name + '.';
};

var bill = {name: 'Bill Watterson'};

alert(calvin.greetFriends.apply(bill, [calvin, hobbes]));
//Bill says hi to Calvin and Hobbes.

In this example, we supply two arguments to the apply function: bill as the context for this and an array with our two person objects. calvin and hobbes become the parameters friendA and friendB in greetFriends.

What if we were to have a method with an unpredictable number of arguments?

Sometimes in a web app you want to make an asynchronous request to a web server, often to a web API to fetch or modify some data. Sometimes you have to make multiple web requests and you don’t know how many because it depends on dynamic user input. Furthermore, you may want to execute some additional code after all the web requests are complete, so you might use a library like jQuery or Q that has a deferral/promise implementation. If you were using jQuery, your code might look something like the following, but with real data and a real purpose, not just to print out numbers:

// example & real world = set variables
var num = Math.ceil(Math.random() * 10),
    delay = 0,
    deferreds = [],
    element = document.getElementById('counter');

// example = print number when deferred is resolved
// real world = make asynchronous GET, POST, PUT, PATCH or DELETE request
function defer() {
    delay += 1000;
    return $.Deferred(function (def) {
        setTimeout(function () {
            element.innerHTML = --num;
            def.resolve();
        }, delay);
    }).promise();
}

// example = execute a random number of deferreds
// real world = execute a number of aync requests based on user input
// both = hold promises in an array
for (var i = num; i > 0; i--) {
    deferreds.push(defer());
}

// example = start off the countdown
element.innerHTML = num;

// example & real world = apply promises--unknown number until runtime
$.when.apply($, deferreds)
    .always(function () {
        // example & real world = take action after all promises are resolved
        element.innerHTML = num + '...done.';
    });

Since this example uses jQuery, I created a JSFiddle you can run to see the result.

References

  1. The four invocation patterns discussed were taken from JavaScript: The Good Parts by Douglas Crockford. I highly recommend reading this short book if you want to get serious about JavaScript.

More JavaScript