LA.NET [EN]

Aug 24

In the previous posts of the series, we’ve started looking at functions. Today we’ll pick up where we left and we’re going to take a deep look at function invocation. I guess that first thing we should understand is what happens when we call a function.

Invoking function X leads to the suspension of the current function so that control can be passed to that new function X. As we’ve seen in the previous post, all functions receive a special argument parameter which contains all the parameters. Besides that special parameter, there’s another one we’ve met when we introduced contexts in JavaScript functions: I’m talking about the this parameter.

At the time, we’ve seen that by default, the this parameter references the global object (which, in an HTML page, references the window object). You can test this by running the following code:

function user(name) {
    //this references global object
    this.name = name;
}
user("luis");
alert(window.name);

Running the previous snippet should lead to a message that shows “luis”.  The important thing to keep in mind here is that with this simple invocation pattern, the this reference will always reference the global object. This might (or might not) be problematic. Lets update the previous snippet so that it uses a helper method:

function user(name) {
    this.name = name;
    function addSomething() {
        return this.name + "++";
    }
    this.name = addSomething();
}
user("luis");
alert(window.name);

If you run the previous snippet, you should see the alert with “luis++”. Everything worked as expected, right? Lets make things more interesting, shall we?

I’m sure you remember (from the previous post) that we can use functions as constructors,right? Let’s do that. Here’s our new code:

function User(name) {
    this.name = name;
}
var user = new User("luis");
alert(user.name);

In this case,the function is invoked as constructor (notice that we’re using the new operator) and is responsible for creating a new object. Notice that this no longer references the global object. In fact, it references the “current” object and that’s why you’ll get the correct name when you pass user.name to the alert method.

We could probably improve our code by adding a helper method that prints the alert message. Here’s one possible option for doing that:

function User(name) {
    this.name = name;
    this.sayHi = function() {
        alert(this.name);
    }
}
var a = new User("luis");
a.sayHi();

Notice that the sayHi function is invoked as a member method of the object. This means that you can be confident that the special this variable references the “current” object and you’ll get a valid value for the name property. btw, the result would be the same if you added the method to the function’s prototype object (and for functions like sayHi, that’s where you should be “putting” your functions).

We’ve already met three invocation scenarios. There’s still one left and to understand it, we’ll need to modify our code. Let’s assume that we’ve got some complicated logic which is “packed” into a helper method and is responsible for performing the final initialization before the object is instantiated. Here’s some dumb code that simulates that:

function User(name) {
    this.name = name;
    //do some other stuff
    function addSomething() {
        this.name = this.name + "++";
    }
    addSomething();
}
var a = new User("luis");
alert(a.name);

Since the helper code will only be used from within the constructor, we’ve opted for using an internal function (the advantage of doing this is that this function will only be callable from within the constructor). Since addSomething is internal, you’ll probably be thinking that this will reference the same object as the User’s this variable. Unfortunately, that doesn’t happen.

To understand why, you need to look at how the method addSomething is invoked. As you can see, it uses the first pattern we’ve seen in this post and we already know that in those scenarios this references the global object. What this means is that the addSomething ends up initializing the name property of the window object with the “++” value (instead of adding that value to the User’s name property). In these fairly simplistic scenarios, Javascript programmers will generally introduce the that variable for letting the addSomething access the correct context (you can think of this a general convention):

function User(name) {
    var that = this;
    this.name = name;
    //do some other stuff
    function addSomething() {
        that.name = that.name + "++";
    }
    addSomething();
}
var a = new User("luis");
alert(a.name);

As you can see, the main difference relies on the that local variable which is reused by the addSomething method. Even though this approach works, what we’d really love is a way to change the context associated to the addSomething. And yes, JavaScript allow us to do that through a 4th function invocation pattern.

Take a look at the following code:

function User(name) {
    this.name = name;
    //do some other stuff
    function addSomething() {
        this.name = this.name + "++";
    }
    addSomething.apply(this);
}

As you can see, by using the apply method we no longer need to introduce a local variable. The first parameter passed to the apply method will be used as the context from within the addSomething function. The apply method expects a second parameter: an array with the values that will be passed to the function. Since the function does not expect any parameters, we simply don’t pass anything when we call the apply method.

Besides the apply method, functions have a second method which allow us to set the context used by that function: I’m talking about the call method. The main difference between the two is that apply expects that the parameters you’re passing to be packed into an array, while call expects one or more parameters separated by comma. For instance, suppose we’re calling an add method. Here’s the code you’d be using with apply and call:

add.apply(context, [par1, par2, par3]);
add.call(context, par1, par2, par3);

As you can see, there’s more to function invocation than you might think of at first. And this concludes our deep dive into functions and contexts. Keep tuned for more on JavaScript.