What is JavaScript `this`?

this is a JavaScript keyword which is vastly misunderstood by a lot of developers and this post aims to bridge that gap. While this article is written for beginners but even experienced developers can use it as a refresher. this is a special identifier keyword which is automatically defined in the scope of every function but what it actually refers to is the real deal. This post is divided into three parts and in the first part, we will talk about what this is and then we’ll talk about some common misconceptions about this and finally, we will see some features of JS related to this keyword.


Prerequisite

I believe learning is like working out and if you don’t warm yourself up properly then you might end up hurting yourself. Thus before talking about this, let’s talk about a few JavaScript concepts which you should be familiar with for this article.

  1. The variables declared in the global scope as var a = 2; are nothing but global-object properties of the same name. Yes, they are not copies of each other but are each other. To make this clearer look at the example below how we are able to access the value of x as the key of the global object window. Further, a thing to must note here is this only works with declarations using var, not let/const.
// This example is supposed to run on Console of any modern browser

var x = "Hello World!";

console.log(window.x); // "Hello World!"
  1. Call-site: The location where a function is called from is called call-site. Usually finding the call-site just comprises of locating the line where the function is called from but this can be misleading as a function can be called at multiple places in the code. Thus the most accurate way to find the call-site is to find the call-stack and then see where the function was called from or in other words what was the previous function in call-stack. Let’s understand this with an example.
function first() {
  // call-stack: `first`
  // call-site: global-scope
  second(); //
}

function second() {
  // call-stack: `first->second`
  // call-site is in `first`
  third();
}

function third() {
  // call-stack: `first->second->third`
  // call-site is in `second`
  console.log("Hello World");
}

first();

What is this?

this is a binding made for each function invocation whose value depends on how and where the function is called. Binding made for this is usually related to variables declared using var and JavaScript running in non-strict mode. Further on the basis of the call-site, we can divide such bindings into four parts as given below:

  • Default Binding

  • Implicit Binding

  • Explicit Binding

  • new Binding

Default Binding: When we call a function in the global scope normally with no special decoration. *this *inside the function gets bound with the global scope. Yes, you can access all global variables using this inside such functions. One thing to notice here is it is only applicable for non-strict mode and variables defined using var.

function doSomething() {
  // "use strict"; // uncomment to run in strict mode
  console.log( this.x )
}

var x = "Hello World!";

// Here the call-site of the function is global scope
doSomething(); // "Hello World!"

Strict mode: The value of this is undefined in this case.

let/const: The variables defined with let/const are not bound.

Implicit Binding: When the call-site of function has a containing object (context object) as given below. That context object is bound to this inside that function. In the example below, obj is the context object and this gets bound to this object and all the keys will be accessible inside the function using this.

function doSomething() {
  console.log( this.x );
}

// This is the context object
var obj = {
  x: "Hello World!",
  y: doSomething
}

obj.y(); // "Hello World!"

Strict mode: This will work the same as non-strict mode.

let/const: Not applicable

Explicit Binding: This binding gives us the superpower to assign this to anything. We can achieve this using call(..) and apply(..) methods. Yes, we can set this to an object, variable, function or anything. But there’s a catch, null and undefined are replaced with global object and primitives are converted into equivalent objects. Whatever we will pass as the first argument that will be used as this binding by the function call, sweet.

function doSomething() {
  // "use strict"; // uncomment to run in strict mode
  console.log( this.x );
}

var obj = {
  x: "Hello World!"
}

var x = "global"

// passing the object to be used for `this` explicitly
doSomething.call(obj);   // "Hello World!
doSomething.apply(obj);  // "Hello World!

// passing the `null` and `undefined`, `this` will be bind to global scope
doSomething.call(null);   // "Hello World!
doSomething.apply(null);  // "Hello World!
doSomething.call(undefined);   // "Hello World!
doSomething.apply(undefined);  // "Hello World!

Strict mode: null and undefined don’t get replaced with the global object and primitives are not converted into objects.

let/const: Not supported when this binds to the global object (either directly or after getting replaced from null or undefined) but variables defined with let/const can be directly passed as this and it will get bound.

New Binding: This one can feel a little odd from the rest of the binding rules. In this kind of binding, if we invoke the function with new keyword and assign it to a variable then this is bound to that variable. Look at the example below for understanding. Here we assign the value of new doSomething() to bar thus inside the function, this gets bound to bar.

function doSomething(a) {
  this.x = "Hello";
  this.y = "World!";
}

var bar = new doSomething();
// `this` has been bound to bar here

console.log(bar.x); // Hello
console.log(bar.y); // World!

Strict mode: No effect

let/const: The variables defined can be both let/const, the same behaviour is observed.


What this isn’t?

Let’s see the most common misconceptions about this which a lot of developers have and dive deeper into each of them. People think this in a function refers to:

  1. Itself

  2. Its Scope

Itself Many developers believe that this refers to the function itself in which it is called which is wrong. Below is a simple example to prove it.

function increase() {
  // increment each time
  this.x++;
}

// Create a property to the function object
increase.x = 0;

// Call function few times
increase();
increase();

console.log(increase.x); // 0 which means this doesn't refers the function itself

Rather the correct way to keep track of how many times increase is called would be to actually use the call method to explicitly define the this keyword to be the increase object (itself). The below-given code example is a working example of it.

function increase() {
  // increment each time
  this.x++;
}

// Create a property to the function object
increase.x = 0;

// Call function few times with using itself as `this`
increase.call(increase);
increase.call(increase);

console.log(increase.x); // 2

Its Scope Another famous misconception developers have is that they think that it refers to the function’s scope. It is a tricky one because in global scope, this refers to the global scope itself but it is quite a misguided statement as we saw that it is not true in the previous section.


More JavaScript Features around this

bind() function: ES5 introduced this function which helps bind the value of this explicitly. bind returns a function with this set to the first argument of bind. This is something similar to call and apply but the difference is once we use bind the function retains its context so that it can be reused.

function doSomething() {
  return this.a;
}

var x = doSomething.bind({a: 'Hello World!'});
console.log(x()); // Hello World!

arrow function: In these functions, this retains the value of the enclosing lexical context’s this. In global code, it will be set to the global object. Let’s see a few examples below:

var doSomething = (() => this.a);

var a = "From Global";
var Context = {a: 'Hello World!'};

// Use bind to set the `this` context
var x = doSomething.bind(Context);
console.log(x()); // From Global

// Use call to set the `this` context
var y = doSomething.call(Context);
console.log(y); // From Global

this in classes: The behaviour of this in classes and functions is the same since classes are nothings but functions under the hood. Still, I would suggest a read here that has a few corner cases. A common convention is to override this behavior so that this within classes always refers to the class instance. This is commonly seen in React Class components.

this as a DOM handler: When a function is used as an event handler, its this is set to the element on which the listener is placed. The code given below will print the DOM element itself.

<button onclick="console.log(this.tagName.toLowerCase())">
  Click to see the name of tag
</button>

  1. You Don’t Know JS: this and Object Prototype

  2. this: MDN docs

, — Dec 6, 2021