Home

Understanding JavaScript this

What is this?

If you come from a class-based, object orientied language such as Java, where this refers solely to a particular instance of the class, it becomes difficult to understand what this in JavaScript does. This especially becomes a problem if you make assumptions that it behaves as it does in most other languages. Why the hell doesn’t it do what it is supposed to? I found myself in one such debacle, due to this code:

  var sources = {
    // contains an array of remote names already in DB.
    registeredRemotes: ['a', 'b'],
    // fetch remote sources through some http call, and store in an array.
    remotes: (function () {
      // return array of remote sources fetched from some server.
      return [{name: 'a'}];
    })(),

    getRemoteSourceNames: function () {
      var sourceNames = [];
      this.remotes.forEach(function(remoteSource) {
        // `this` no longer refers to sources object.
        if (this.registeredRemotes.indexOf(remoteSource.name) != -1) {
          sourceNames.push(remoteSource.name);
        }
      });
      return sourceNames;
    }
  };

  sources.getRemoteSourceNames();

This code snippet outputs an error in the form of:
TypeError: Cannot read property 'indexOf' of undefined
After tracing it, I realized that this.registeredRemotes is undefined, since this here no longer refers to the sources object. Why? In order to understand this, one should understand what the hell execution context in JavaScript means, and what this is evaluated to. Strict and Non-strict mode also determines what thisevaluates to.

The value of this is decided when an execution context is created. When the original context where this is declared changes, what this refers to also changes.

In my case, an easy fix was to create a variable to hold the value of this prior to the forEach, then using that variable in my anonymous callback. This is because in the anonymous callback, the value of this is the global object.

  ...

  getRemoteSourceNames: function () {
    var sourceNames = [];
    var self = this;
    this.remotes.forEach(function(remoteSource) {
      if (self.registeredRemotes.indexOf(remoteSource.name) != -1) {
        sourceNames.push(remoteSource.name);
      }
    });
    return sourceNames;
  }

Execution Context

You have probably heard this term being thrown around, but like me, have no idea what it means.
Execution context refers to the environment in which a piece of JavaScript code executes. It is evaluated into one of three environments:

  • Global execution context - the default, implicit environment, where your code is not part of any function. Any variable which is created while in this context becomes a property of the global object. In a browser, the global object is the window object.

  • Function execution context - context that is created in order to run the code within function, then destroyed after the function’s execution.

  • Eval context - eval() is a function of the global object which evaluates Javascript code which is represented as a string. When an eval() statement is called, the code it runs exists in the eval context. Use of eval is generally discouraged, as most usecases can be easily mapped to other JavaScript constructs, though you might need it.

Fantastic. Now that we know what an execution context is, we can look at the different cases where this attains different values.

Global Context

In this context, this always refers to the global object. This holds true both in strict and non-strict mode. One thing to note is that in non-strict mode, the value of this must always evaluate to an object while in strict mode, if the value of this is undefined, it will stay undefined.

  // beginning of app.js file
  this === window // true

Function Context

This is where this fuckery starts.

As a constructor
When a function is used as a constructor to create a new object using the new keyword, this refers to the resultant object. The function returns the this object in this case, except when some other object is returned.

  var PersonA = function () {
    this.name = 'Some Name';
  };
  var person = new PersonA();
  // this refers to the person object.
  person.name === 'Some Name' // true

  var PersonB = function () {
    this.name = 'Some Other Name';
    return { name: 'Entirely different'};
  };
  person = new PersonB();
  person.name === 'Entirely different' // true

In a simple function call
In non-strict mode, this will evaluate to the global object. In strict mode, this remains what it was when execution went into the function context.

  function afunc() {
    return this;
  }
  var whatIsThis = afunc();
  whatIsThis === window // true

  function bfunc() {
    'use strict';
    return this;
  }
  whatIsThis = bfunc();
  whatIsThis === window // false
  whatIsThis === undefined // true

As an Object Method
If a function is called as a method of an object, this is set to the object where the method is defined. This also holds true even if the function was defined somewhere up in the object’s prototype chain. this will still refer to the object where the method is called on.

  var anObj = {
    someFunc: function () {
      return this;
    }
  };

  anObj.someFunc() === anObj // true

Using the bind method
The bind method, which all functions inherit from Function.prototype, creates a new function with the body and scope of the function on which bind was called. In this case, this is permanently set to the first argument while calling bind.

  function afunc() {
    return this;
  }
  bfunc = afunc.bind('alright');
  bfunc() == 'alright' // true

While in the DOM

You have probably done something like:

  <button onclick="alert(this.textContent);">Hello</button>

When this is used to handle an inline on-event, it is set to the DOM element on which the event was placed, as long as this exists in the outer function’s context. When a function is used as an event handler, this becomes the target of the event i.e. the element from which the event was fired from.

  function alertOnClick (e) {
    this === e.target // true
    this === button // true
  };
  var button = document.getElementById('aButton');
  button.addEventListener('click', alertOnClick);

In ES6/ES2015

ES6, the version of JavaScript which was ratified in 2015, introduced arrow functions which bring their own flavour to this. In arrow functions, this is permanently set lexically, to the outer execution context. What does this mean? It is basically a fancy way of saying, it uses this from the surrounding code. Also note that the this of an arrow function cannot be changed, even by using the bind function.

  var someFunc = (() => this);
  someFunc() === window // true

  var someObj = {
    someInnerFunc: function () {
      return (() => this);
    }
  };
  someObj.someInnerFunc()() === someObj // true

A note on jQuery

In jQuery, various methods’ callbacks are bound to certain contexts, for convenience. For instance, in jQuery’s $.each method, this refers to the current element.

  $('p').each(function () {
    this.textContent // refers to each paragraph's text content.
  });

Also, while selecting DOM elements using the jQuery selector, jQuery accepts a second element which specifies what is to be used as context, which further affects the value of this. I would recommend browsing through jQuery’s docs for your particular use cases.

What now?

this might seem like it is all over the place, but mastering its various faces and what it evaluates to enables you to avoid lots of headache while using it. Moving into ES2015 with its classes (which are still apparently prototype based?), a firm understanding of this makes the transition easier, since you know what’s going on even with the new syntax.

comments powered by Disqus
comments powered by Disqus