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: <a href="http://dojotoolkit.org/reference-guide/1.9/dojo/_base/lang.html#hitch">lang.hitch</a>
. 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.