Skip to main content

Command Palette

Search for a command to run...

Simplifying "this" in JavaScript

Elaborating the JavaScript "this" with examples.

Updated
8 min read
Simplifying "this" in JavaScript
S

A lifelong learner. Love to travel. Listen to music.

Overview

The this keyword can be one of the most confusing concepts in JavaScript. We will explain this this in layman's terms with enough examples.

this is the object the function is a property of

One liner,

obj.someFunc(this);

Here, this is in the function someFunc and the someFunc is a property of obj. So this represents the obj.

Examples

Local Object

const myObject = {
  name: 'foo',
  task: function () {
    return `This is ${this.name} task.`;
  }
};

myObject.task();

This will return the text This is foo task..

By definition, here this refers to the object myObject. The function of task is the property of myObject.


Global Object

In the global scope, this is the window object.

In the global scope, if we define a function

function a() {
  console.log(this);
}

It will print the window object.

Since the a() method is window.a(), so according to our first definition, for a(), this is the window object.


"this" is determined by Who Called the method?

this is defined by the object called the method.

const a = function () {
  console.log('For a, this is ', this);
  const b = function () {
    console.log('For b, this is ', this);
    const c = {
      hi: function () {
        console.log('For c, this is ', this);
      }
    };
    c.hi();
  };
  b();
};

a();

In this case, both a and b has the window object as this.

And for c, since it is invoked by c, then, the this is the c object.

Now, query might be, how b() has the this object window, not the a. Because b() is not invoked by in similar way a.b(). Instead, it is invoked like window.a(b())


"this" is not lexically scoped, it is dynamically scoped

const obj = {
  name: 'Billy',
  sing() {
    console.log('a', this);
    var anotherMethod = function () {
      console.log('b', this);
    };
    anotherMethod();
  }
};

obj.sing();

Here for a, the this context is obj.

For b, the this context should be obj. But since this is not lexically scoped and follows method calls, b has the this context of window.

This is why this follows the dynamic scoped instead of lexical scoped.


Preserve this context

We can preserve this context,

  • By preserving it in a variable

  • Using the arrow method

  • Using bind method (Ex. first bind and then obj.prop()())

To solve this dynamic scope issue, we can use the arrow method. Arrow method is bound to the lexical scoped.

Using the Arrow method:

const obj = {
  name: 'Billy',
  sing() {
    console.log('a', this);
    var anotherMethod = () => {
      console.log('b', this);
    };
    anotherMethod();
  }
};

obj.sing();

Here both a and b have the this context of obj.

Using bind:

Another was to make the this to lexical scoped using bind() method.

const obj = {
  name: 'Billy',
  sing() {
    console.log('a', this);
    var anotherMethod = function () {
      console.log('b', this);
    };
    return anotherMethod.bind(this);
  }
};

obj.sing()();

Here a and b has the this context of obj.

Preserving in lexical scope:

Holding the this in another object can be used to preserve the this context.

const obj = {
  name: 'Billy',
  sing() {
    console.log('a', this);
    var self = this;
    var anotherMethod = function () {
      console.log('b', self);
    };
    anotherMethod();
  }
};

obj.sing();

Here both a and b have the this context of obj.

Manipulating "this"

We can manipulate/change the this keyword using the following methods,

  • call

  • apply

  • bind

Using call method:

We can use use the call method to invoke a method, like the following,

var myMethod = function () {};
// following both statements are similar
myMethod();
myMethod.call();

We will use the same mechanism to inject the this,

const wizard = {
    name: 'Wizard',
    health: 50,
    heal() {
      this.health = 100;
    }
};

const archer = {
    name: 'Archer',
    health: 30
};

wizard.heal.call(archer);
console.log(archer);

This will print,

{
    name: 'Archer',
    health: 100
}

Here the health property is the same as the wizard object.

Now, let's see another example of passing parameters using the call method.

const wizard = {
  name: 'Wizard',
  health: 50,
  heal(param1, param2) {
    this.health = this.health + param1 + param2;
  }
};

const archer = {
  name: 'Archer',
  health: 30
};

wizard.heal.call(archer, 10, 20);
console.log(archer);

This will add the params 10 and 20 with it existing value 30.

So the printed value is,

{
  name: 'Archer',
  health: 60
}

Using apply

apply() is similar to call(), it uses call() underlying. The only difference between call() and apply() is, in apply(), the parameter is passed through the parenthesis.

var myMethod = function () {};
// following both statements are similar
myMethod();
myMethod.apply();

We can update/change the this using the apply, as follows,

const wizard = {
  name: 'Wizard',
  health: 50,
  heal(param1, param2) {
    this.health = this.health + param1 + param2;
  }
};

const archer = {
  name: 'Archer',
  health: 30
};

wizard.heal.apply(archer, [10, 20]);

console.log(archer);

This will print the same value as the previous call() method.

{
  name: 'Archer',
  health: 60
}

Using bind

Except for the call() and bind(), the bind() does not invoke the method instantly. Instead, it returns a method that can be invoked later.

const wizard = {
  name: 'Wizard',
  health: 50,
  heal(param1, param2) {
    this.health = this.health + param1 + param2;
  }
};

const archer = {
  name: 'Archer',
  health: 30
};

const archerHeal = wizard.heal.bind(archer, 10, 20);
archerHeal();

console.log(archer);

This will print the same value as the previous call() or apply() method.

{
  name: 'Archer',
  health: 60
}

We can make use of bind and reach an interesting pattern. Let's we have a method that takes two numbers and returns the multiplied result.

const multiplyMethod = (num1, num2) => {
  return num1 * num2;
};

Now using the function bind, we will create two methods from the previous method. Both methods will provide only one parameter.

  • One will return multiply with 4

  • Another will return multiplied with 10

let multiplyByTwo = multiplyMethod.bind(this, 4);
let multiplyByTen = multiplyMethod.bind(this, 10);

console.log(multiplyByTwo(2));
console.log(multiplyByTen(2));

This will return,

8
20

Benefits

Allow methods to use the properties of the object itself

const myObject = {
  name: 'foo',
  task: function () {
    return `${this.name} task.`;
  },
  doTask: function () {
    return `Do ${this.task()}`;
  }
};

myObject.doTask();

This will return Do foo task.

Execute the same code for multiple objects

function showMyName() {
  console.log(this.name);
}

const foo = {
  name: 'foo',
  showMyName
};

const bar = {
  name: 'bar',
  showMyName
};

foo.showMyName();
bar.showMyName();

This will return

foo
bar

Brain Teaser

Let's observe, a couple of examples

Example 01:

const myObj = {
  name: 'myName',
  myMethod() {
    console.log(this);
  }
};

myObj.myMethod();

Here the this is the myObj itself.

Example 02:

const myObj = {
  name: 'myName',
  myMethod() {
    return function () {
      return console.log(this);
    };
  }
};

myObj.myMethod()();

Since the return function is not called by the myObj, here the this object is the window. It is using dynamic scope instead of lexical scope.

Example 03:

const myObj = {
  name: 'myName',
  myMethod() {
    return () => {
      return console.log(this);
    };
  }
};

myObj.myMethod()();

Since, the arrow method strictly maintains the lexical scope, here the this represents the myObj.

Example 04: Classes are by default `use strict;`

In ES2015 classes, this behaves as it does in object methods. It refers to the instance of the class.

class Person {  
    constructor(name) {    
        this.name = name;  
    }
    showThis() {    
        console.log(this);  
    }
}
const person = new Person('John');
person.showThis(); // Person {name: 'John'}
const showThisStandalone = person.showThis;
showThisStandalone(); // `undefined` because all parts of a class' body are strict mode.

Example 05: When Multiple Rules Apply [Rules from summary]

When multiple rules apply, the rule higher on the list wins.

var obj1 = {
    value: 'hi',
    print: function() {
        console.log(this);
    },
};
var obj2 = { value: 17 };

If rules 2 and 3 both apply, rule 2 takes precedence.

obj1.print.call(obj2); // -> { value: 17 }

If rules 1 and 3 both apply, rule 1 takes precedence.

new obj1.print(); // -> {}

Summary

  1. If the new keyword is used when calling the function, meaning the function was used as a function constructor, the this inside the function is the newly-created object instance.

  2. If this is used in a class constructor, the this inside the constructor is the newly-created object instance.

  3. If apply(), call(), or bind() is used to call/create a function, this inside the function is the object that is passed in as the argument.

  4. If a function is called as a method (e.g. obj.method()) — this is the object that the function is a property of.

  5. If a function is invoked as a free function invocation, meaning it was invoked without any of the conditions present above, this is the global object. In the browser, the global object is the window object. If in strict mode ('use strict';), this will be undefined instead of the global object.

  6. If multiple of the above rules apply, the rule that is higher wins and will set the this value.

  7. If the function is an ES2015 arrow function, it ignores all the rules above and receives the this value of its surrounding scope at the time it is created.

References

  1. GreatFrontend

  2. Arnav Aggrawal's article on Medium