Understanding JavaScript Context

October 20, 2011 at 8:54 pm

JavaScript’s functional scoping makes the language incredibly fun and flexible. You can seamlessly adopt almost any paradigm with ease. It does come with one caveat: confusion over this. Because only functions create scope, and because the context in which functions execute is mutable, what this actually points to is confusing at first.

Let’s take a simple Java class.

Using this template we can create a new student and print out the relevant information.

Student me = new Student("Joshua", 26);
System.out.println(me);

In these methods, this refers to the instance of Student that we created. this.name is "Joshua" and this.age is 26. The function is always executed against the current object. this is a predictable known quantity. Coming from this background JavaScript’s flexibility in regard to this and function context comes as a shock.

Let’s take a quick example.

Looks standard enough, right? The prototype is extended to add a function that interacts with the base object, and existing variables are used to add functionality. Straight forward non-async JavaScript will rarely have an issue with scope. Asynchronous code will often have scope issues.

Extending this example to add an asynchronous call to a hypothetical remote service checking a minimum drinking age.

The potential issue is on fn(this.age. Here this will not be pointing to the Student object like many expect. Instead it points to the global context. Here is a slightly different method that allows us to specify the context.

Here’s what’s happening. First the drinking_age function has lost the age parameter and been modified to look in the current context for it instead (this.age). Then when invoking Laws.drinking_age we are using .call, one of the three scope-changing functions inherent in all Function objects.

  • .bind(this, [, arg1, arg2, ...argN])
  • .apply(this[, arguments])
  • .call(this[, arg1, arg2, ...argN])

Each of these has a distinct role in adjusting the function context. .bind returns a new function with new context. .apply will execute a function in a context with an optional array to expand into arguments. .call executes a function in a context with individual arguments.

Using this structure, we can even manipulate the contexts of anonymous functions on the fly to persist current scope without messy var self = this; statements.

jQuery('.messages').hide(250, messages.cleared.bind(this));

Understanding scope is fundamental to an enjoyable experience writing JavaScript.

§

October 2011