this Keyword

One of JS’s most powerful mechanisms is also one of its most misunderstood: the this keyword. One common misconception is that a function’s this refers to the function itself. Because of how this works in other languages, another misconception is that this points the instance that a method belongs to. Both are incorrect.

As discussed previously, when a function is defined, it is attached to its enclosing scope via closure. Scope is the set of rules that controls how references to variables are resolved.

But functions also have another characteristic besides their scope that influences what they can access. This characteristic is best described as an execution context, and it’s exposed to the function via its this keyword.

Scope is static and contains a fixed set of variables available at the moment and location you define a function, but a function’s execution context is dynamic, entirely dependent on how it is called (regardless of where it is defined or even called from).

this is not a fixed characteristic of a function based on the function’s definition, but rather a dynamic characteristic that’s determined each time the function is called.

One way to think about the execution context is that it’s a tangible object whose properties are made available to a function while it executes. Compare that to scope, which can also be thought of as an object; except, the scope object is hidden inside the JS engine, it’s always the same for that function, and its properties take the form of identifier variables available inside the function.

  1. function classroom(teacher) {
  2. return function study() {
  3. console.log(
  4. `${ teacher } says to study ${ this.topic }`
  5. );
  6. };
  7. }
  8. var assignment = classroom("Kyle");

The outer classroom(..) function makes no reference to a this keyword, so it’s just like any other function we’ve seen so far. But the inner study() function does reference this, which makes it a this-aware function. In other words, it’s a function that is dependent on its execution context.

NOTE:
study() is also closed over the teacher variable from its outer scope.

The inner study() function returned by classroom("Kyle") is assigned to a variable called assignment. So how can assignment() (aka study()) be called?

  1. assignment();
  2. // Kyle says to study undefined -- Oops :(

In this snippet, we call assignment() as a plain, normal function, without providing it any execution context.

Since this program is not in strict mode (see Chapter 1, “Strictly Speaking”), context-aware functions that are called without any context specified default the context to the global object (window in the browser). As there is no global variable named topic (and thus no such property on the global object), this.topic resolves to undefined.

Now consider:

  1. var homework = {
  2. topic: "JS",
  3. assignment: assignment
  4. };
  5. homework.assignment();
  6. // Kyle says to study JS

A copy of the assignment function reference is set as a property on the homework object, and then it’s called as homework.assignment(). That means the this for that function call will be the homework object. Hence, this.topic resolves to "JS".

Lastly:

  1. var otherHomework = {
  2. topic: "Math"
  3. };
  4. assignment.call(otherHomework);
  5. // Kyle says to study Math

A third way to invoke a function is with the call(..) method, which takes an object (otherHomework here) to use for setting the this reference for the function call. The property reference this.topic resolves to "Math".

The same context-aware function invoked three different ways, gives different answers each time for what object this will reference.

The benefit of this-aware functions—and their dynamic context—is the ability to more flexibly re-use a single function with data from different objects. A function that closes over a scope can never reference a different scope or set of variables. But a function that has dynamic this context awareness can be quite helpful for certain tasks.