Functions and methods in JavaScript

JavaScript allows the creation of global functions, as in the simple example

function multiply(x, y) {
  return x * y;
}

This function can be called from anywhere using syntax such as var product = multiply(4, 5) which assigns the value 20 to product.

However, all functions are also objects and, as such, a function can be attached to another object as a data field. Functions that are fields of other objects are usually called methods.

Suppose we define  an object like this:

var number = { value: 0 }

This creates a single object called number which has a single data field called value. We can attach a method to number either as part of its definition, as in this attempt to define a method that squares number’s value:

var number = {
  value: 0,
  square: function() {
    return value * value;
  }
}

number.value = 42;
number.square();

If we run this code (try it in Chrome’s console), you probably won’t get the result you’re expecting. The call to number.square() returns NaN (not a number) rather than the expected value of 42 * 42 = 1764. What went wrong? Analogous code in a language like C# or Java would work, since we’re (kind of) treating number like a class with a data field and a method, and as we know, a class’s methods all have access to the class’s data fields.

JavaScript, as you’ve probably guessed, doesn’t work quite that way. If we refer to an unadorned variable like value, it is assumed that this is a global variable, so the square() method above looks for a global value and, not finding it, returns the indeterminate result of NaN.

You can test this by defining a global value variable and giving it a distinctive value, as in var value = 12; If you now run the line number.square() you’ll get the result 144, so it’s clear where the square() method is getting its ‘value’ from.

To solve this problem, we need to realize that any method gets a ‘bonus’ parameter passed to it, which is the object that calls the function. This object is referred to inside the function by the keyword this, which you’re probably familiar with from C# or Java. However, although using this as a prefix in C# is optional, in JavaScript it’s compulsory if you want to refer to the object calling the method. Thus our revised definition of number looks like this:

var number = {
  value: 0,
  square: function() {
    return this.value * this.value;
  }
}

number.value = 42;
number.square();

Now if we run the last 2 lines again, we get the expected result of 1764.

In fact, global functions are also passed a this parameter, except this time it refers to the global object, and use of this here actually is optional. For example, we could define the global function

function doubleValue() {
  return 2 * this.value;
}
doubleValue();

Running this code gives the result 24, since this.value now refers to the global value. We could equally well have defined this function as

function doubleValue() {
  return 2 * value;
}
doubleValue();

We’d get the same result.

This behaviour makes sense for truly global functions, but we run into problems if we declare functions inside methods. A rather contrived example is a revised version of the square function above. Suppose we define a new method for number called newSquare:

number.newSquare = function () {
  var innerSquare = function () {
    return multiply(this.value, this.value);
  }
  return innerSquare();
}

number.newSquare();

If number.value is still 42 and the global value is 12, running this code produces the value 144. Now what’s wrong? Even though we’ve included the this prefix when referring to value, the global value is still being used instead of number.value.

The problem here is what is widely regarded as a mistake in the design of JavaScript. Whenever a function that is not a field of an object (that is, a method) is called, this is always bound to the global object. The function innerSquare is a utility function declared within a method, but is not a method itself, so its this is the global object, not the number object that calls newSquare.

This sort of code design is very common, so there is a standard workaround to avoid this problem. Inner functions such as innerSquare do have access to local variables inside their container method, so we can define a local variable (usually called that) and assign it to point to this. We can then use that whenever we want to access the object that called the method. We therefore rewrite newSquare as follows:

number.newSquare = function () {
  var that = this;
  var innerSquare = function () {
     return multiply(that.value, that.value);
  }
  return innerSquare();
}

number.newSquare();

This time we get the correct result of 1764.

Advertisements
Post a comment or leave a trackback: Trackback URL.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: