Few people learn JavaScript as a first programming language. Typically, people will start with an object-oriented language such as C#, Java or C++. These languages have a few things in common, such as curly braces, objects, functions, if
-statements and loops. Another common feature of object-oriented programming languages is the ‘this
’ keyword. It refers to the class which contains the currently executing code.
It can be something of a surprise then when a programmer moves on to JavaScript and discovers that the value returned by ‘this
’ is less predictable than expected.
There are five different cases where you might use ‘this
’. These include using it in the global namespace, and four different ways of using it inside a function, as described by Douglas Crockford in JavaScript – The Good Parts.
‘this’ in the Global Namespace
In the global namespace, that is, outside of any function, ‘this
’ refers to the window object. I don’t see any reason why anyone would want to use ‘this
’ in the global namespace, in fact it’s probably a bad idea, nevertheless it can be done and so is worth mentioning for completeness.
Scenario 1 – Method Invocation
In JavaScript (and many other languages), a method is a function which is declared inside of a class or object. Referring to ‘this
’ inside of a method refers to the containing object. Therefore in the example below, we are alerted with ‘Roberto Martinez
’ rather than ‘Howard Kendall
’.
var manager = 'Howard Kendall';
var everton =
{
yearFounded : 1878,
inEurope : true,
manager : 'Roberto Martinez',
getManager : function()
{
return this.manager;
}
};
alert(everton.getManager());
This is the kind of behavior you would expect coming from an object-oriented background, so no unpleasant surprises here.
Scenario 2 – Function Invocation
Here we are referring to any function which is not declared inside an object. That is, any function which is declared in the global namespace. Using ‘this
’ inside such functions actually refers to the global namespace, rather than the containing function. Strange behaviour indeed, and Douglas Crockford actually describes this behaviour as a mistake by the creators of JavaScript. Thus in the example below, we are alerted with ‘Howard Kendall
’, rather than ‘Roberto Martinez
’.
var manager = 'Howard Kendall';
var getManager = function()
{
var manager = 'Roberto Martinez';
return this.manager;
};
alert(getManager());
So we have established that ‘this
’ inside of a method refers to the method’s containing object, and ‘this
’ inside of a global function refers to the global namespace. But what is referred to by ‘this
’ inside of a function which is declared inside of a method? The answer is – the global namespace, as shown in the example below.
var manager = 'Howard Kendall';
var everton =
{
yearFounded : 1878,
inEurope : true,
manager : 'Roberto Martinez',
getManager : function()
{
var innerFunction = function()
{
return this.manager;
};
return innerFunction();
}
};
alert(everton.getManager());
This behaviour is slightly surprising. If we want to use an inner function inside of a method, we can get around the problem by assigning ‘this
’ to a variable inside the outer method, as shown below with the ‘that
’ variable.
var manager = 'Howard Kendall';
var everton =
{
yearFounded : 1878,
inEurope : true,
manager : 'Roberto Martinez',
getManager : function()
{
var that = this;
var innerFunction = function()
{
return that.manager;
};
return innerFunction();
}
};
alert(everton.getManager());
Scenario 3 – Constructor Invocation
A third function scenario is that of the constructor function, that is, a function which is invoked with the ‘new
’ keyword. When a function is invoked in this way, the ‘this
’ keyword refers to the object created, even if the constructor function was defined in the global namespace. Therefore, in the example below, we are alerted first with ‘Howard Kendall
’ and then with ‘Joe Royle
’. We refer to ‘this
’ inside of a global namespace function when we set the ‘manager
’ variable inside of ‘getEverton
’, but as we call the function with the ‘new
’ keyword, the global namespace ‘manager
’ variable is not updated.
var manager = 'Howard Kendall';
var getEverton = function()
{
this.yearFounded = 1878;
this.inEurope = true;
this.manager = 'Joe Royle';
this.getManager = function()
{
return this.manager;
};
};
var efc = new getEverton();
alert(manager);
alert(efc.getManager());
The same behaviour is displayed when we call a method declared on the constructor function’s prototype, and properties can be set on our new object in the normal way.
var getEverton = function()
{
this.yearFounded = 1878;
this.inEurope = true;
this.manager = 'Joe Royle';
};
var efc = new getEverton();
getEverton.prototype.getManager = function()
{
return this.manager;
};
alert(efc.getManager());
var efc2 = new getEverton();
efc2.manager = 'Harry Catterick';
alert(efc2.getManager());
So to summarize constructor invocation, it reflects typical object-oriented behavior more closely than function invocation. However, it can be dangerous to rely on this approach, as if we forget to use the ‘new
’ keyword, our constructor function will be invoked as a regular global function, and we will see the behavior described above in scenario 2.
Scenario 4 – Apply Invocation
Our final function invocation scenario is the apply invocation pattern. The apply method is defined on the function prototype, therefore it can be invoked on any function. This approach to invocation allows us to provide any object we want to represent ‘this
’ inside of the function. Its first argument is bound to ‘this
’, and its second argument is an optional array of parameters to be passed to the function. Thus in the example below, we are first alerted with ‘Joe Royle
’ and then with ‘Harry Catterick
’.
var everton =
{
yearFounded : 1878,
inEurope : true,
manager : 'Roberto Martinez',
getManager : function()
{
return this.manager;
}
};
var getEverton = function()
{
this.manager = 'Joe Royle';
};
var efc = new getEverton();
var efc2 = new getEverton();
efc2.manager = 'Harry Catterick';
alert(everton.getManager.apply(efc));
alert(everton.getManager.apply(efc2));
I have attempted here to clearly describe the different behaviors you might see when using the ‘this
’ keyword in JavaScript. However, it is a difficult area to describe in plain English. If you find my descriptions confusing, I recommend reading Crockford’s book. All the code examples are available to play with at this jsfiddle.
The post Something Like This appeared first on The Proactive Programmer.