Context - the Magic Argument
A lot of common newbie problems with ECMAScript come from a misunderstanding of what "this" is and what it does.
Basically, "this", also known as the "context," is a hidden argument passed to every function when it is called. Python and CLOS programmers will be familiar with this concept already. To make things easier, one can envision every function as passing this explicitly:
function foo(a, b) {
return a + b;
}
... might actually look like (pseudo-code, "this" is a reserved word):
function foo(this, a, b) {
return a + b;
}
The context does not need to be specified in the argument list, nor is it included in the "arguments" collection, nor explicitly passed when called; however, it can be accessed within the function as if it had by the name "this". A function called thus:
var a = {
b: function(c) {
return c + this.d;
},
d: 5
};
a.b(1); // returns 6
... is the equivalent of (pseudo-code again):
var a = {
d: 5
};
function b(this, c) {
return c + this.d;
}
b(a, 1); // returns 6
The object before the last dot in the lookup sequence for the function is passed as the context. If no such object exists (as in a simple foo() call), the global object ("window" in a browser) is passed. In the global scope, outside any functions, the context is the global object.
There are two functions to pass a context explicitly: Function.prototype.call and Function.prototype.apply. call() takes a variable number of arguments, using the first as the context and the rest as the explicit arguments, like this (above example of passing the context explicitly, but not pseudo-code this time):
var a = {
d: 5
};
function b(c) {
return c + this.d;
}
b.call(a, 1); // returns 6
apply() is exactly the same, but, usefully, takes an array of arguments as the second argument rather than a variable number of arguments.
Functions passed as callbacks can have unexpected contexts. A common newbie mistake is to call a function that takes a callback and expect its context to be preserved:
setTimeout(a.b, 3000);
This will not work. The lookup done on "a" on this line is completely transparent to setTimeout(). All it sees is the function reference; the context is not preserved, and "window" is passed. Instead, a closure must be used to pass the object to pass as context later:
setTimeout(
function() { a.b(); },
3000);
A function to do this generically can be found at bind().