JavaScript for C# developers: calling functions and the ‘this’ variable

I can’t believe that I haven’t posted an article on how to call functions in JavaScript and what this gets set to for each of the various invocation patterns. It’s one of those things that catches people out periodically, so it’s well worth discussing at length.

Lego123There are four different ways to call or invoke a JavaScript function: method invocation, function invocation, constructor invocation, and apply invocation. Each of these invocation patterns results in this being set to a different object inside the function. In C#, there is never any doubt what this refers to: it’s the object (the instance) on which the method is called. Using this is entirely optional, unless you are trying to resolve some possible ambiguity in the code (say, a parameter to the instance method has the same name as a field of the instance: in this case, to refer to the field you would have to use this). Of course, for a static method rather than an instance method, using this is an error and will be caught at compile-time.

Method invocation

Back to JavaScript. We’ll first talk about the equivalent to C#’s calling of an instance method: method invocation. This is just like the C# case: you’ve got some object and you call a method of that object. The this variable inside the function will be set to point to the object you’re calling the method on. In essence, pretty much exactly the same as C#.

Let’s look at a simple object called printer. When you call the log method of printer passing in some other object, it will print the object passed in as well as the number of times the printer object has been used.

var printer = {
  callCount : 0,

  log : function(obj) {
    console.log("---");
    console.log(obj);
    this.callCount += 1;
    console.log("--- (log number: " + this.callCount + ")");
  }
};

As you can see, a very simple object with one field (callCount) and a log method. The log method will print the object, increment the field, and then print a line showing how many times it has been called. The use of this is not optional as in C#; it must be used, otherwise the interpreter will try to resolve it by walking up the scope chain to the Global Object (which in browser JavaScript is window). If you like, this bypasses the normal scoping rules in JavaScript.

Here’s a quick example of how to call it.

var someObj = {a:42};

printer.log(someObj); // prints 1 as log number
printer.log(someObj); // prints 2 as log number

The fun thing about JavaScript is that functions are objects. We can pass them around at will. Here’s a new (empty) printer object that I then create the same field for. I can then create a log method by copying the printer.log function.

newPrinter = {};
newPrinter.callCount = 41;
newPrinter.log = printer.log;

newPrinter.log(someObj); // prints 42 as log number

Function invocation

The next way of calling a function is using function invocation. This is easy in concept – just call the bare function, not using an object – but it is the invocation pattern that causes the most problems and bugs. The reason is that the this variable gets set to the Global Object. Let’s take a look at a couple of examples. Firstly though, let’s define a simple function that returns true if the passed in object is the Global Object.

var isGlobalObject = function(obj) {
  return (function() {return this === obj;}());
};

What? Did he say simple? Let’s take it slowly. First of all, isGlobalObject takes in an object and returns something. What it returns is the result of automatically calling an anonymous function. This anonymous function is called using the function invocation pattern we’re currently discussing and checks to see if this is equal to the outer obj parameter. In other words, this anonymous function makes use of the pattern we’re trying to demonstrate. (Note: because I did not hard-code window, this function will also work on node.js, where the Global Object is called something else.)

Before making moving on, let me emphasize a point. Automatically calling an anonymous function is one of the great patterns of modern JavaScript and hence in one of these types of functions this will point to the Global Object.

Now we can make up some example function and show that calling it using the function invocation sets this to the Global Object.

var doSomething = function (arg) {
  isGlobalObject(this) ?
    console.log("In this function, 'this' is the Global Object") :
    console.log("In this function, 'this' is something else");
};

doSomething(); // prints that 'this' is the Global Object

There is another common JavaScript coding pattern that can throw up bugs because people assume this points to the enclosing object rather than the Global Object, and that’s with callbacks. Let’s illustrate with setTimeout. I’ll write a ticker object that outputs an asterisk once a second.

var ticker = {
  show : function() {
    console.log("*");
  },
  start : function() {
    this.show();
    setTimeout(function() { this.start(); }, 1000);
  }
};

ticker.start();

If you run this code, you should see an asterisk, and then after a second you should get an error saying “this.start is not a function”. The reason is that the callback to the setTimeout function is an anonymous function and setTimeout’s code will call it using the function invocation pattern. The this variable will be set to the Global Object and not the ticker object, despite what a quick glance at the code might imply. Since window.start does not exist, you get the error message.

To solve this bug, we shall have to save the value of the this variable in the start method, and then use that local variable inside the anonymous function. Because in JavaScript scope is defined by function, the anonymous function will ‘see’ this local variable in its parent function.

var ticker = {
  show : function() {
    console.log("*");
  },
  start : function() {
    var self = this;
    self.show();
    setTimeout(function() { self.start(); }, 1000);
  }
};

ticker.start();

Notice that every time start is called, it is called using the method invocation pattern and we always save the object it’s called on in a local variable called self.

Constructor invocation

The third way of calling a function is using the constructor invocation pattern. The first point to make here is that you must use the new keyword when calling the function.

var Person = function(lastName, firstName) {
  this.firstName = firstName;
  this.lastName = lastName;
};

Person.prototype.print = function() {
  var name = this.lastName + ", " + this.firstName;
  console.log(name);
};

var me = new Person("Bucknall", "Julian");
me.print();

Here I’ve declared a function called Person, created a method via its prototype object, and then called the function using new. What this does is similar to C#: it creates a new object based on the template I gave. Person is not a class, even though creating a Person object looks like you’re creating an instance of a class. In essence, the new keyword will create a brand new empty object, set its constructor to the function, and then call the constructor. The this variable will be set to this new empty object.

The final unusual thing about constructor invocations is that the newly created and initialized object is returned by default; you don’t have to code up a return statement explicitly.

The big problem about the constructor invocation pattern is that you sometimes forget to use the new keyword. Constructors are normal functions, there’s nothing in the language that marks them as being anything special, and so suddenly the call turns into a function invocation. And we know all about those: this points to the Global Object. In my example: calling Person without new will create a two new properties on the Global Object (firstName and lastName), return and set me to undefined, and then crash on the next statement since undefined certainly has no method called print.

There is a bit of a hack that can get round this for you. Define your constructors like this, and you’ll avoid the problem of forgetting to use new.

var Person = function(lastName, firstName) {
  if (!(this instanceof Person)) {
    return new Person(lastName, firstName);
  }
  
  this.firstName = firstName;
  this.lastName = lastName;
};

In other words, if the this parameter is not an instance of Person assume that the caller forgot the new keyword and construct a new object properly and return it. If not, just proceed as normal for a constructor.

Apply invocation

The final way to call a function is to use the function’s apply or call methods. This is known as apply invocation. Yes, a function is an object, so it can have (and has) properties and methods of its own. Both apply and call work by calling the function on an explicit object that you supply. Arguments to the function are either passed one-by-one (call) or as an array (apply). (The way I remember which is which is to say Apply uses an Array because they both start with A.) The this variable is set to the first parameter of the call to call or apply. Here it is in action with our printer object from above:

var anotherPrinter = { callCount : 23 };
var someObj = { foo : 42 };

printer.log.call(anotherPrinter, someObj);
printer.log.apply(anotherPrinter, [someObj]);

All that’s happening here is that I’ve created an object (anotherPrinter) with a callCount field, and then applied the printer.log method to it, passing in some object to log.

Album cover for Geometry Of LoveNow playing:
Jarre, Jean Michel - Pleasure Principle
(from Geometry Of Love)
Loading similar posts...   Loading links to posts on similar topics...

2 Responses

 avatar
#1 J.D. Mullin said...
20-Dec-11 8:51 AM

Good article, Julian. Any comments or patterns applicable to callbacks you provide to third party components? For example, in a button click handler "this" is often the button, not your object. These callbacks can often be confusing when they live in the same js file with the object (perhaps that's the problem...). Multiple functions/methods in the same js file then have a different context for "this". Thoughts?

julian m bucknall avatar
#2 julian m bucknall said...
03-Jan-12 12:08 PM

J.D.: I would have to say that if you were writing a library where it's likely that a this problem would occur ("I'm expecting this to be the button but it's window dammit") you should invoke the callback through the apply invocation and not simply call it. I talked about this kind of pattern here.

Cheers, Julian

Leave a response

Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.

  •  Emphasize with italics: surround word with underscores _emphasis_
  •  Emphasize strongly: surround word with double-asterisks **strong**
  •  Link: surround text with square brackets, url with parentheses [text](url)
  •  Inline code: surround text with backticks `IEnumerable`
  •  Unordered list: start each line with an asterisk, space * an item
  •  Ordered list: start each line with a digit, period, space 1. an item
  •  Insert code block: start each line with four spaces
  •  Insert blockquote: start each line with right-angle-bracket, space > Now is the time...
Preview of response