Dojo FAQ: What is the benefit to using run-time binding with lang.hitch?

By on September 25, 2013 1:41 pm

A concept that is often confusing to new JavaScript developers is the idea of function context, specifically as it relates to the value of this. For example, methods in Java are bound to classes at compile time, and this in a method call will always refer to an instance of the method’s class. The context of a JavaScript function, on the other hand, is determined not by where the function was defined, but by how it was invoked.

Two of the most common ways JavaScript functions are invoked are on an object and standalone. When a function is invoked on an object, like foo.bar(), this will point to the object the function was invoked on (i.e., foo). When a function is called standalone, like bar(), this will refer to the global scope.*

Consider the code below, which registers a callback to respond to a click event on a DOM element:

var myObject = {
  stateValue: false,

  handleClick: function(event) {
    alert(this.stateValue);
  }
};

require([ 'dojo/on' ], function(on) {
  on(someElement, 'click', myObject.handleClick);
});

This code seems fairly straightforward. When someElement is clicked, myObject.handleClick will be called and an alert should pop up with the value of myObject.stateValue. However, when a user actually does click someElement, the alert that pops up contains the text “undefined”. What’s going on?

At first glance, it looks like on is being given the combination of myObject and handleClick, and a developer might expect that the function will be called as myObject.handleClick(). However, consider an equivalent version of the code on line 10:

var handleClick = myObject.handleClick;
on(someElement, 'click', handleClick);

In actuality, only handleClick is begin passed to on. When handleClick is eventually called for a click event, it will not be invoked on myObject.

Luckily, Dojo has a solution to ensure that a function is always called with the expected context: lang.hitch. Given a context object and a function, lang.hitch creates a new function that calls the original using the given context object, just like Function.prototype.bind in ECMAScript 5. Like Function.prototype.bind, lang.hitch allows you to specify additional arguments that will be passed to the original function. We can use lang.hitch to ensure that the value of this refers to myObject as expected:

require([ 'dojo/on' ], function(on) {
  on(someElement, 'click', lang.hitch(myObject, myObject.handleClick);
});

Unlike Function.prototype.bind, lang.hitch supports an additional binding technique that uses a function name instead of a function as its second argument:

require([ 'dojo/on' ], function(on) {
  on(someElement, 'click', lang.hitch(myObject, 'handleClick');
});

When lang.hitch is used in this way, the bound function will retrieve handleClick from myObject by name each time it is invoked. Aside from being slightly more compact, this form of lang.hitch creates bound functions that will automatically incorporate changes to the context object. For example, if a new function were assigned to the handleClick property of myObject, a binding made using lang.hitch with a function name would automatically make use of the new function, while a binding made with a function object would need to be recreated.

Now that a context object has been correctly bound to the click callback, the example above works as expected. That was easy! lang.hitch is just one example of the many features Dojo provides to make writing complex applications simpler.

* In ECMAScript 5 strict mode, this is undefined if the function is called without a context.