Skip to main content

Nailing Down The Meaning Of "This" In JavaScript

Sense-certainty itself has thus to be asked: What is the This? If we take it in the two-fold form of its existence, as the Now and as the Here, the dialectic it has in it will take a form as intelligible as the This itself. To the question, What is the Now? we reply, for example, the Now is night-time. To test the truth of this certainty of sense, a simple experiment is all we need: write that truth down. A truth cannot lose anything by being written down, and just as little by our preserving and keeping it. If we look again at the truth we have written down, look at it now, at this noon-time, we shall have to say it has turned stale and become out of date.  
Hegel, Phenomenology of Spirit S. 95

Overview

In philosophy, English words like "this" are referred to as indexicals, with the implication that (A) they are used to point at something and (B) what they point at is determined by context (just as, when I point at someone, who I am pointing at is determined by who is in front of me).  The keyword "this" in JavaScript shares these characteristics -- it functions like a variable that refers or points to some value the programmer / API desires to keep track of, but what the value is changes depending on the context in which "this" appears.

We can think of the reference of "this" in JavaScript as being determined under four distinct heads: (1) globally; (2) implicitly (or contextually); (3) functionally; (4) through the use of the "new" keyword.  Explanations and examples follow:

1. Global Reference

The default value for "this," outside of any specific context (what a specific context amounts to, we will under the second heading), is the "global" object, which is ultimately determined by the context in which the script is being interpreted.  In a browser, for instance, this global object is the "window", whereas in node this is an object called "global" (appropriately enough).  You can confirm this by opening up a console in the development tools and entering "this," or opening node and entering "this."  What the window or the node global are is another matter, but suffice it to say that using "this" in a browser can give you access to the DOM, information about size of the window width and height, and many other things.  On the other hand, these variables are all accessible in the global namespace, which leads to an interesting fact about the global "this" -- you can use it to access and set the values of global variables.

"Use Strict"

Using "this" to set the values of global variables seems, at the very least, redundant, and in fact is not always possible -- case in point, if you implement "strict mode".

If you include the string "use strict" at the top of a code block, the global object will not be accessible to "this" within that code block.  Strict mode can thus be invoked either at the beginning of a script or within, say, a function.  (One -- to me -- unintuitive feature of this is that appearance in a function does not constitute a context change.  Within a function and outside of strict mode, this will still refer to the global object.  I suppose the reason that this is unintuitive is that within a function (or other code block), which variables are accessible does change -- and this change is significant enough to make a function or code block feel like a new context.)

(That "this" is not available for global contexts in strict mode seems to fall out from the more general feature of strict mode, that it does not allow variables to be accessed until they are declared.  This may seem like commonsense, but technically, for example, mis-spelling a variable name in JavaScript and using that variable creates a new global variable.  My reasoning is that "this" only has a reference in context, and that in strict mode, the global object is removed as an available context -- meaning that "this" will by default be undefined and unavailable.)

2.  Implicit / Contextual Reference

The global use of this having been identified and explaining, the keyword's notoriety derives from the way it functions when used contextually ("in the wild"), the relevant context being the object.  When used within an object, "this" refers to the object itself.  The flexibility of its objects is, at least for me, what makes JavaScript unique (and versatile!), but in this case, it also leads to headaches.

A simple example:

let person = {
    first: "Alex",
    getFirst: function() {
        return this.first
    },
    determineContext: function() {
        return this === person
    }
}

We define the object "person" with the intention that we can call "getFirst" to get one of its attributes.  If we are used to using Python or a heavy-duty OOP language like Java, we might be misled by analogy into thinking that "this" here functions much as "self" or else is set to give us the object as a whole.  And mostly, this analogy serves us -- but the versatility of JavaScript can get us into some sticky situations.  We can "import" functions defined in the "person" object into new contexts where this is not what we originally intended it to be:

// true...
person.determineContext()
let notInKansasAnymore = this.determineContext
// false!
//notInKansasAnymore()

"this" isn't married to its original context, so when we run a function that contains it in a new context, the reference of "this" changes!  "But this example is a little contrived?  When is this ever an issue?"  Issues with "this" can come up when developing in frameworks like React or Angular (2).

class Foo extends React.Component{
  constructor( props ){
    super( props );
  }
  handleClick(event){
    console.log(this); // <==== right here
  }
  render(){
    return (
      <button type="button" onClick={this.handleClick}>
        Click Me
      </button>
    );
  }
}

Let's use our imagination: the developer of this component wanted to manipulate and maybe display information stored in the component (say on its state) when the user clicked a button.  She came to React from Java development and assumed, because she was working in the context of a class, that she could use "this" anywhere within the class to access its contents.  Unfortunately, when she tried to run this code in the browser, the console gave her an error: the object she was trying to access didn't exist.  She might have had to spend some time puzzling over what was wrong when she had clearly declared and retrieved the information she was interested in -- only to realize that in the end that, because of the way that React works, the "handleClick" function was executed in a new context -- so that, because of the way JavaScript works, the reference of this changed.

Experiments

A couple things are worth noting in passing -- but I'll leave the reader to investigate on his or her own.  First, JavaScript is flexible, and objects sometimes act like classes, other times store data.  So you can have an outside object that you think of more like a class (maybe you even define it like a class) and an inside object that you think of more as a data-storing attribute -- perhaps with a couple of functions thrown in for good measure, because you can do that.  But the context changes from the outside object to the inside object, and when you call a method on a nested object, that method gets the nested object's "this."  Imagine if the "person" we defined above had a "father" with its own methods.  Calling "person.father.someMethod" would give us the "father"'s this, not the "person"'s.

Second, changes in context are again not always intuitive.  For the longest time (indeed, up to the very point I was writing this post), I thought that "console.log" would change the context for this -- after all, you appear to be accessing "this" from within the console object.  But we need to be careful to distinguish what we're calling from, I guess, where we're calling it.  When I call "console.log" and give it some arguments, I'm doing that from, for instance, the global context, or else within a class or a function or a function defined in a class or whatever.  The context I'm calling it from is what gives "this" its meaning.  I was misled probably because I was thinking of "this" too much after the analogy of its use in an English sentence, where the words surrounding it can change its meaning.  When coding, it isn't the code surrounding "this" itself that determines its meaning, but rather from where that code is executed / where it is called.

3.  Reference Determined By Function

JavaScript's developers seem to have been aware that "this" does not always function intuitively, and have accordingly included functions designed to tame it when it behaves erratically.  Before I really understood these functions, I was fond of declaring

let that = this;

circumventing the "this" problem by using a standard variable, which would behave more predictably, to capture the necessary context.  The workability of such solutions is limited, however, because the newly declared variable will be limited in scope to the code-block in which it was declared -- and sometimes what should be the standard context for this needs to be accessed across contexts, for instance from different functions defined on an overarching class. That's when these functions are necessary.

"Call" And "Apply"

These methods have (as far as I can tell) identical functionality but slightly different syntax.  Both are used to call an external method and supply that method with an alternative reference for this.  The syntax is either

anotherMethod.call(thisArg, param_1, ... , param_n)

or

anotherMethod.apply(thisArg, [param_1, ... , param_n])

This is a little bit like supplying a mock when doing testing in Python -- instead of being called with its native context supplying the reference for this, the target method is called with the reference required.

Call Me By Your Name

Here is an example of "call" at work:

var person = {
    firstName: "Alex",
    add: function(a, b) {
        console.log(`This is ${this.firstName}'s calculation: ${a + b}`)
    },
    sayHi: function() {
        return "Hello, I'm " + this.firstName
    },
    determineContext: function() {
        return this === person
    },
    friend: {
        firstName: "Alice",
        sayHi: function() {
            return "Hello, I'm " + this.firstName
        },
        determineContext: function() {
            return this === person
        }
    }
}

// Hello, I'm Alex (!)
person.friend.sayHi.call(person)

Normally, calling "person.friend.sayHi" would use "person.friend" as its context, but we have circumvented that context and made it use "person."  We could pass in any object we want -- through "this" or a variable that captured a "this" -- or a raw object like "{ firstName: "Xiaoxu" }".

"Bind"

"bind" is used syntactically like apply, but unlike apply, rather than invoking the function it is called on, it returns a new version of that function with the desired value for "this."  Using "bind", we can solve the React problem we discussed above:

class Foo extends React.Component{
  constructor( props ){
    super( props );
    this.handleClick = this.handleClick.bind(this) <==== fix the reference
  }
  handleClick(event){
    console.log(this);
  }
  render(){
    return (
      <button type="button" onClick={this.handleClick}>
        Click Me
      </button>
    );
  }
}

Again, I don't want to get too much into why we need to do this here -- suffice it to say that React, as written, invokes "handleClick" in a different context than we would expect; by dictating the context for "handleClick" with "bind", we nip potential problems in the bud.

Use Case: Alarm

Though this is not directly related to "this", another great use case for "bind" is in functions that accept callbacks, since calling bind on a function supplies us with a callback readymade.  For example, "setTimeout" takes as parameters a callback and an integer.  "setTimeout" then executes the callback after the number of milliseconds designated by the integer.  You sometimes encounter a problem, though, with functions that accept callbacks: you want to pass them a given function, but with arguments you determine in advance.  Unfortunately, supplying the function with the preferred arguments by invoking it immediately calls that function (not to mention slotting the wrong argument into the original function call -- whatever calling your target function returns).  This is where the "bind" method comes into play.  I created a small timer here that takes advantage of this functionality.

4.  The "New" Keyword

The last context for "this" is, so to speak, a created context -- it comes into being when we use the keyword "new."  The "new" keyword is probably a topic that deserves its own post / article, but broadly speaking, we have to remember the way that JavaScript implements "classes."  Classes in JavaScript, such as they are, are something like functions that have been turned into objects.  And the "new" keyword is what we use to generate one of these objects.  Compare:

function considerThis() {
  this.whatIsThis = function() {
    console.log(this)
  }
  this.name = "considerThis"
  whatIsThis()
}
considerThis()

In the code above, calling "considerThis" does not print an object with whose "name" attribute has the value "considerThis" -- it does not print the considerThis object; rather, it prints the window.  That changes, however, we we apply the "new" keyword:

c = new considerThis()
// { ... name: "considerThis" ... }
c.whatIsThis()

This sufficiently demonstrates, I think, the way "new" functions -- it takes a function that has no special context to speak of and generates an object with its own "this".

(The example is, granted, a little bit hokey -- precisely because in defining the considerThis function, we must straddle the line between function and object / class.  Normally when defining a class, one does not include a function call in the main body of that class -- though the class may have a method that calls one of its (static) functions.  In JavaScript, however, we can do exactly this, just because between a class and a function there is not a sharp distinction.  One side effect of the example I have chosen -- and it is the best I can think of, at least "to order" -- is that creating the new object c does at the same time call the "considerThis" function and so log the old value of "this" -- but perhaps that just makes the demonstration, that the behavior of the function defined in it changes, all the more visceral.)

This And "=>" Notation

I believe that what I have said above is correct, at least so far as the examples go.  But with JavaScript, nothing is ever as clear as you'd think it could be.  I already touched above, briefly, on using a variable to capture the context of "this" -- I would now like to discuss another method, introduce with arrow notation, for getting around "this" references.

First an admission: above I said, and for all the examples I gave I was correct to say, that "this" in the context of a function refers not to that function but the context in which it was defined.  That being said, consider the following example, another timer:

function Person() {
  this.age = 0;
  setInterval(function growUp() {
    this.age++;  }, 1000);
}
p = new Person()

Based on what I said in the last section, you'd think that "p" would age rapidly.  In fact, p proves to be ageless, even after minutes have gone by.  What is happening?  When in doubt, try logging this:

function Person() {
  this.age = 0;
  setInterval(function growUp() {
    this.age++; console.log(this); }, 1000);
}
p = new Person()

As it turns out, "this" refers to the "window" -- the global object.  What?  Didn't we instantiate "p"?  And as we saw above, didn't that change the reference of this from the global object to the new object?  Not quite.  It turns out that every function does indeed get its own "this" -- even if a function's "this" does not refer to that function itself.  Within setInterval, an anonymous function is declared.  The context for that anonymous function is, apparently, the global context.  So every time "growUp" is called, we attempt to increase the "age" of the "window" object, not "p".

But why does that happen?  Since the function is declared (albeit within a function call) within another function, and that function, become an object, gets its own "this" when invoked with the "new" keyword, shouldn't the context become the newly created object?  Isn't that what happened in the example from the last section?

First, it is crucial to note that the function from the example in the last section was defined in the context of this, or implicitly / contextually (see #2).  So when the creation of a new object redefined "this", it also redefined the implicit context.  But second, remember that "p" is no longer a function, but an object -- and it doesn't make much sense (to me anyway) to think of an object that makes a function call.  After all, when does it make that call?  It never gets invoked.  What does get invoked, however, is the function "Person", and when "Person" is invoked and, in its turn, invokes "setInterval", it is working with the global context.  Going back to an earlier point, that global context is what "setInterval" then gets for any arguments that might be defined within it when it is called -- i.e. the function "growUp" (which might as well be an anonymous function).  The context of "this", again, is determined by the situation in which it is invoked, not by its syntactic trappings.

If all that is true, and the reason this is happening is because of the circumstances in which "setInterval" was invoked -- before and outside of the context of the new object -- then why doesn't calling "Person" with the "new" keyword also set the global "age" to 0 as a side-effect?  This explanation may seem ad hoc, but perhaps that's the function of the "new" keyword -- it creates a new object and sets assignments to "this" to that new object.  What it doesn't take care of is function calls.  But I guess all of this is just circumlocution around the overall point -- functions get their own "this".

All that being said, we now have a problem.  We want the timer we have created and start when we create a new "Person" to refer to that "Person"'s age, not the global age.  To do this, we need a way of creating a function, anonymous or otherwise, that, instead of getting a separate "this", gets the same "this" as the context in which it is declared.  That way, if the "this" that belongs to its context changes, its "this" will change as well.  This is the purpose of arrow notation.  If we define our "Person" thus

function Person() {
  this.age = 0;
  setInterval(growUp = () => {
    this.age++; console.log(this); }, 1000);
}
p = new Person()

"p" will age as expected.

Comments

Popular posts from this blog

Getting Geodata From Google's API

The apps I'm going to be analyzing are part of Dr. Charles Severance's MOOC on Python and Databases and work together according to the following structure (which applies both in this specific case and more generally to any application that creates and interprets a database using online data). The data source, in this case, is Google's Google Maps Geocoding API.  The "package" has two components: geoload.py  and geodump.py .  geoload.py  reads a list of locations from a file -- addresses for which we would like geographical information -- requests information about them from Google, and stores the information on a database ( geodata.db ).  geodump.py  reads and parses data from the database in JSON, then loads that into a javascript file.  The javascript is then used to create a web page on which the data is visualized as a series of points on the world-map.  Dr. Severance's course focuses on Python, so I'm only going to work my way through ...

Compiling and Executing Java Files With -cp

I decided I was going to "man up" and figure out how to compile a java program with an external dependency from the command line instead of relying on an IDE-- the DOS command line, to be more specific. I ran into a few problems: 1.  The external dependency was given to me as a java file.  I experimented compiling it as a .jar, but I wasn't sure how to import a class from a .jar, so I ended up compiling it into a class. 2.  When I tried to run the file, I got an error saying that the class had been compiled with a different version of Java than my JRE.  The Internet told me to check my path variable for Java.  It sure looked like it was pointing to the latest JRE (and the same version of Java as my compiler).  I asked the Internet again and found the following command: for %I in (java.exe) do @echo %~$PATH:I I'm not exactly sure what the syntax of that magic command is (intuitively it's returning the path that executes when I run the "java" com...

Quick Find / Quick Union (Connected Nodes)

Setup This week I learned about the "Quick Find" or "Quick Union" algorithm. Imagine an NxN grid of nodes, some of which are connected by lines. A connection can be interpreted as accessibility: if two nodes are connected, you can get from one to the other. Every node is accessible to itself: to get where you already are, stay there. Also, If you can get from A to B, you can go back from B to A. And if you can get from A to B and from B to C, then you can get from A to C. As a consequence, the connection between nodes divides the grid into regions of mutually accessible nodes. You can travel from any node in a given region to any other node in that region -- but not to any nodes outside that region (exercise to reader -- proof by contradiction). The problem has two parts. First, find a way to represent this grid structure and the accessibility relation; second, use your schema to efficiently calculate whether two given nodes are accessible to each other. ...