DEV Community

Abdullah Al Numan
Abdullah Al Numan

Posted on

Interview Question: Explain how `this` works in JavaScript [in 3 mins].

In JavaScript, this is a property of the execution context in which a function is executed.

The explanation for how this gets evaluated is very elaborate, and it is covered case by case in this MDN article comprehensively.

The value of a function's this is determined mostly by how the function is being called in the call site, rather than how the function is defined: things like whether strict mode is enabled or not, whether the function is defined and called standalone or not, whether we are calling the function as a method of an object or if we are extracting a reference of an object method and then calling it somewhere else, etc.

Execution Context

A function's execution context is the environment in which the function is executed at runtime. It includes, the variable scope, function arguments and the value of this object.

this

If we need a function which acts on the properties of an object we want to use, its this ought to be that object. In other words, our function's target object has to be made available to the execution context at runtime, so that we are able to access it with this.

In normal mode, this is always an object. undefined and null values are autoboxed to the global object (the window object in the browser). In strict mode, however, it can be undefined or null, as there is no autoboxing of this in strict mode.

function testThis() {
  return this;
};
console.log(testThis()); // [object Window]

function testThisInStrictMode() {
  'use strict'
  return this;
};
console.log(testThis()); // undefined
Enter fullscreen mode Exit fullscreen mode

In Objects

If we have an object with a method that uses this and we call the method on the object, the object is automatically assigned to the method's this.

const person = {
  name: 'Abd',
  age: 42,
  sayHi: function() {
    return `Hi, this is ${this.name}`;
  },
};

console.log(person.sayHi()); // "Hi, this is Abd"
Enter fullscreen mode Exit fullscreen mode

The same applies to instances of custom objects created using constructor functions, as well as classes.

// constructor function example
function Person() {
  this.name = 'Abd';
  this.age = 42;
  this.sayHi = function() {
    return `Hi, this is ${this.name}`;
  };
};
const person = new Person();
console.log(person.sayHi()); // "Hi, this is Abd"

// class example
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  };

  sayHi() {
    return `Hi, this is ${this.name}`;
  };
};

const person = new Person('Abd', 42);
console.log(person.sayHi()); // "Hi, this is Abd"

Enter fullscreen mode Exit fullscreen mode

Function References

Probably the most highlight-able case about this in JavaScript arises when we want to extract a reference of a method from an object and then call it from elsewhere.

For example, if we store the sayHi() method of the person object (from any of the examples above) in a variable and then invoke it later on, we will not have any object set for the method to act on. We are effectively detaching the object from the referenced function, so this for this function at runtime will be either the global object or undefined depending on whether in normal mode or strict mode.

`use strict`
const sayHiAbd = person.sayHi; // Note that person.sayHi is NOT being invoked here
console.log(sayHiAbd()); // Error: Cannot read property 'name' of undefined

Enter fullscreen mode Exit fullscreen mode

In this scenario, sayHiAbd() is like a standalone function defined as follows:

function sayHiAbd() {
  return `Hi, this is ${this.name}`;
};
Enter fullscreen mode Exit fullscreen mode

In such cases, we have to call the function using .call() or .apply() in order to set the this object explicitly at the call site.

console.log(sayHiAbd.call({name: 'Abd', age: 42})); // "Hi, this is Abd"
Enter fullscreen mode Exit fullscreen mode

Binding Permanently

If we want to permanently bind an object to the function, we have to create a new function with .bind(), which attaches the object to the new function.

const alwaysSayHiAbd = sayHiAbd.bind({name: 'Abd', age: 42});
console.log(alwaysSayHiAbd()); // "Hi, this is Abd"
Enter fullscreen mode Exit fullscreen mode

Arrow Syntax

The arrow syntax permanently binds the enclosing lexical context of the function definition to its execution context. So, the call site context never meddles with arrow functions.

In the object literal person example above, if we modify our sayHi() function to return an arrow function that returns the greeting string, the returned (arrow) function's this gets bound to its enclosing lexical context, which is the person object itself. Storing a reference to it and calling it always points its this to person.

const person = {
  name: 'Abd',
  age: 42,
  sayHi: function() {
    return () => `Hi, this is ${this.name}`;
  },
};
const sayHiAbd = person.sayHi();
console.log(sayHiAbd()); // "Hi, this is Abd"

Enter fullscreen mode Exit fullscreen mode

References

  1. this
  2. ECMAScript standard - ResolveThisBinding

Top comments (0)