Many caveats clutter the JavaScript language. Some of them are quite hilarious and relatively harmless, but few can get really nasty and lead to insidious bugs. Today, I’m gonna talk about something from the second group: the semantics of this
keyword in JavaScript.
this
?It is worth noting why JS has the this
keyword at all. Normally, we would expect it only in those languages which also have the corresponding class
keyword. That’s what C++, Java and C# have taught us: that this
represents the current object of a class when used inside one of its methods. It only makes sense, then, to use this
keyword in a class scope, denoted by the class
keyword – both of which JavaScript doesn’t seem to have. So, why’s this
even there?
The most likely reason is that JavaScript actually has something that resembles traditional classes – but it does so very poorly. And like pretty much everything in JS, it is written as a function
:
Here, the Greeting
is technically a function and is defined as one, but semantically it works more like constructor for the Greeting
“class”. As for this
keyword, it refers to the object being created by such a constructor when invoked by new
statement – another familiar construct, by the way. Additionally, this
also appears inside greet
method and does its expected job, allowing access to the text
member of an object that the method was called upon.
So it would seem that everything with this
keyword is actually fine and rather unsurprising. Have we maybe overlooked something here, looking only at half of the picture?…
Well yes, very much so. And not even a half but more like a quarter, with the remaining three parts being significantly less pretty – to say it mildly.
this
It might not have been immediately visible, but in the previous example the this
keyword has been used in two different meanings. They just happen to blur into single semantic, thanks to our associations with other object-oriented languages. Incidentally, those associations were probably the main reason behind adding the whole this
/new
mechanism to JavaScript. Unfortunately, it doesn’t look quite as appealing when viewed in the context of language as a whole. Because at this wider perspective, this
sucks big time.
For starters, here are the possible referents of this
, i.e. things it can point to depending on where and how the keyword is used:
new
keyword, as seen in the Greeting
function abovegreet
function abovewindow
objectWhat you might have noticed is the massive drop of sanity level between the second and third option. There is absolutely no mistake here: this.x = 1;
can mean the same as window.x = 1;
and create a global variable, happily cluttering our global namespace. In fact, this is the default behavior of this
when used inside a function. It’s the first two, non-crazy variants that require a special call syntax, while the following “just works”:
Chances are, it doesn’t work the way we would expect it to work.
Time for a small brain teaser. Could you compare the Greeting
and foo
functions, and find some differences between them? Preferably, it should be a difference which is unrelated to naming and would persist even if we compiled minified both functions… Can you find at least one?
You could try for a while, but it would ultimately prove futile: there are none. Nothing is special about the so-called constructor of our Greeting
“class”: it is an ordinary JavaScript function. While it seems to exhibit some class-like behavior, it is only due to our usage of new
. If we called it directly, we would end up with the exact same behavior as with foo
example above: this
would bind to window
and text
would end as a global variable.
Not only this is surprising, confusing and error-prone, but it also generalizes to every case where this
is used inside a function! The gruesome conclusion is that:
The meaning of
this
depends on how the function was called, regardless of how it was defined.
To realize how unpleasant consequences this may have, consider a simple object that has a “method”:
Let’s say that we want counter.inc
to be called in response to some external event. In asynchronous, callback- and event-based environments – where almost all of the JavaScript code operates – this would be an extremely common occurrence. Setting aside any specific details, we can safely assume that our method will at some point end up in a variable. It will be deep inside the guts of a framework or library of choice, but in the end, it will be essentially equivalent to this line:
When the event in question actually happens, callback
is of course invoked:
And it is invoked like a normal function (rather than a method), so this
acquires meaning specific to that invocation. Which means the global (window
) object, not the counter
object as we would expect… Yuck!
Fortunately, this can be worked around in a relatively straightforward way: by capturing counter
object in a closure, therefore ensuring that it ends up as this
inside its inc
method:
Just think, though: what’s the chance that someone notices the “superfluous” function later on and decides to “refactor” it out?… If you ever use this trick in practice, make sure you add some adequate comment.
this
and thatWhat about the fourth point, stating that this
can actually refer to anything at all? Again, it is only a matter of how the function gets called – and it so happens that there are some very special ways of calling a function. Those are the apply
and call
methods, where one can completely specify not only the argument list, but also the referent of this
inside the function:
Don’t be mistaken: this is a very cool feature, especially when used appropriately (and judiciously) in some well-known libraries:
Problems arise when we already have this
in our outer function, because it will be inaccessible (“overwritten”) in the inner one. As a remedy, it is suggested to capture the outer this
in a local variable that will come as part of inner function’s closure:
Conventionally the variable is often called that
, although self
is also popular for somewhat obvious reasons :)
this
doesn’t look prettyAs we can see, this
keyword in JavaScript is not quite as simple as in many other languages. Care must be taken when calling any function that uses it internally; otherwise we might end up with totally unintended results.
This article needs to “use” more “strict”!
function foo(arg) {
return this;
}
foo() === window;
But:
"use strict";
function foo(arg) {
return this;
}
foo() === undefined;
This is a very useful passage /again on xion.log/
Did you ever think of publishing a guide/course similar to your C++ megatutorial, but this time commercially and in English? I believe you would stand a firm competition to Wrox and apress, as quick examples…
Anyway thanks for the C++ work and for these brief microtutorials herein:-)