This, That, and Another Thing
Once you've started working with JavaScript objects, you'll quickly need to become very familiar with a special JavaScript variable: this
. It's often thought of as referring to a specific instance of an object, but it may be better thought of as providing a context for executing a function. Here's a simple example of using this
with a constructor function. Let's say we want to create a counter object with its own increment function.
// We define a counter object with two properties: the // count, and a function to increment the count. function Counter() { this.count = 0; this.increment = function () { this.count++; } } // Here's how we call the function to increment the // counter's value. var counter = new Counter(); counter.increment();
Within a constructor function, this
is used to refer to the new object that's being initialized. The object will have a count
property and an increment
method. The increment
method uses this
to refer to the object's count
property. Easy-peasy, right?
Perhaps. But let's slow down for just a second. this
on line 4 and this
on line 6 appear to both reference the new object - and they do - but they do so in subtly different ways. The increment
method is scoped separately from the Counter
constructor function. It's this
variable is actually assigned when we call the increment
method on line 13 - not when we execute the constructor function. Since we call the increment
method by using the dot operator on the counter
variable, the increment
method's this
variable references counter
. Again, JavaScript does not assign this
on line 6 when the object is being initialized.
So, what does all this mean? It means in that simple example, we've seen two ways to set the value of JavaScript's this
variable. When calling a constructor function, this
references the newly created object. When calling a function through the dot operator, this
references the object whose dot operator was used to call the function. This is what allows us to create prototype functions that are shared across multiple objects.
function Counter() { this.count = 0; } Counter.prototype.increment = function () { this.count++; } var counter1 = new Counter(); counter1.increment(); var counter2 = new Counter(); counter2.increment();
Clearly, the value of this
within the increment
function cannot be assigned using the value from within the constructor function. Rather, it references counter1
or counter2
, depending on which Counter
instance is used with the dot operator, thereby ensuring that we modify counter1.count
or counter2.count
.
However, JavaScript allows us to create more complex examples where the value of this
may not be so apparent. Consider this code sample:
function Counter() { this.count = 0; setInterval(function () { this.count++; }, 1000); } var counter = new Counter();
After a quick glance, it looks like a Counter
is initialized with a zero count
and its value increments every second when the interval callback is executed. Unfortunately, that's not actually what happens.
Every JavaScript function has its own instance of the this
variable, which is independent of the this
variable in any other function. In the above example, this
on line 5 is independent of this
on line 2 because it occurs within a different function scope. Previously, if we wanted this
within a nested function to reference a particular object, we had to use that object's dot operator when referencing the function. However, in this instance, the function is not an object property and we don't even perform the function call. It's being called from some unknown location inside JavaScript's implementation of setInterval
. So how can we access the object's count
property if not through this
? We need to find another way to do so and a common solution is to introduce a that
variable.
function Counter() { var that = this; this.count = 0; setInterval(function () { that.count++; }, 1000); }
JavaScript allows a function to access any variable that exists within its own scope - or within any scope that encloses that function (a feature called closure). In this example, the anonymous callback function is enclosed by the Counter
constructor function, which means the callback can reference any variables declared within Counter()
. Though the callback can't reference Counter's
this
variable directly (since it's been hidden by the callback's own this
variable, if we assign Counter's
this
variable to some other variable (i.e. that
), we can access the count
property through the other variable. It is a common practice in JavaScript to declare a that
variable for the purpose of referencing an object from within enclosed functions. (A popular alternate name used instead of that
is self
, but it can really be any name you might choose - it's just a normal variable.)
But if every function has its own this
variable, what does this
actually reference within the callback function? Well, it may depend. Often, callback functions are executed such that this
refers to JavaScript's window
object. In the case of setInterval
, that's exactly what happens. This is why it can be helpful to think of this
as providing a context for a function's execution rather than referencing a particular type of object instance. The callback function may be executed within the context of the entire browser window
instead of within the context of a specific object instance. It's even possible that a function will be executed within other contexts as well, depending on how the function is actually being invoked. The popular jQuery library was designed to invoke event callback functions such that this
references the particular DOM element that generated the event.
As a way of avoiding unexpected this
assignments in callback functions, JavaScript does provide a way to bind a function to a particular value of this
.
function Counter() { this.count = 0; setInterval(function () { this.count++; }.bind(this), 1000); }
In this code sample, the function that is passed to setInterval
has a small tweak. Following the function's closing curly brace, it has been bound to the value of this
from the constructor function. The value that's passed to the bind
function will be assigned to the this
variable within the callback function. This is one way to avoid any surprises with callback functions without creating a that
variable.
There is much more that could be said about this
and callback functions. Hopefully, this brief introduction will at least help you remember that functions may need to use this
, but sometimes that
, and maybe even another thing entirely. It all depends on your context.