DEV Community

Cover image for A Quick Guide To this Keyword In JavaScript: What this Is And When
Alex Devero
Alex Devero

Posted on • Originally published at blog.alexdevero.com

A Quick Guide To this Keyword In JavaScript: What this Is And When

Many JavaScript developers try to avoid using the this keyword. One reason is that what this refers to changes. This guide will help you with it. You will learn what this keyword refers to in specific contexts. This will make it easier for you to work with it and help you predict what to expect when you use it.

Quick introduction

The this is a special keyword in JavaScript. There is one problem JavaScript developers struggle with when they learn about this. It can have different values. It can refer to different things. What determines what this refers to is context, the context at which you use it. Let's take a look at these contexts and what to expect from this keyword in each of them.

Strict and sloppy mode

In JavaScript, there are two modes or variants of JavaScript you can work with. The first one is strict mode. The second one is sloppy mode. By default, you write your JavaScript code in a sloppy mode. This mode is more ... sloppy. It allows you to do things that would be forbidden in a strict mode. These things would not work.

JavaScript offers you an option to switch from sloppy mode to strict mode. You can do this by using 'use strict' statement at the beginning of your code. Any code that follows after this statement will automatically follow the rules and restrictions of strict mode. This also includes the this keyword.

Global scope

When you are in a global scope, the this keyword will refer to the global object window. This is the case at least if you are in the browser. If you are in a Node.js environment, the this will refer to global object called global. In a global scope, it doesn't matter if you are in a sloppy mode or a strict mode.

// Global context example no.1: sloppy mode
console.log(this)
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}

// In Node.js:
console.log(this)
// Output:
// <ref *1> Object [global] {
//   global: [Circular *1],
//   clearInterval: [Function: clearInterval],
//   clearTimeout: [Function: clearTimeout],
//   setInterval: [Function: setInterval],
//   setTimeout: [Function: setTimeout] {
//     [Symbol(nodejs.util.promisify.custom)]: [Getter]
//   },
//   queueMicrotask: [Function: queueMicrotask],
//   clearImmediate: [Function: clearImmediate],
//   setImmediate: [Function: setImmediate] {
//     [Symbol(nodejs.util.promisify.custom)]: [Getter]
//   }
// }


// Global context example no.2: strict mode
// Switch to strict mode.
'use strict'

console.log(this)
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}

// In Node.js:
console.log(this)
// Output:
// <ref *1> Object [global] {
//   global: [Circular *1],
//   clearInterval: [Function: clearInterval],
//   clearTimeout: [Function: clearTimeout],
//   setInterval: [Function: setInterval],
//   setTimeout: [Function: setTimeout] {
//     [Symbol(nodejs.util.promisify.custom)]: [Getter]
//   },
//   queueMicrotask: [Function: queueMicrotask],
//   clearImmediate: [Function: clearImmediate],
//   setImmediate: [Function: setImmediate] {
//     [Symbol(nodejs.util.promisify.custom)]: [Getter]
//   }
// }
Enter fullscreen mode Exit fullscreen mode

Functions

When it comes to [functions], the mode at which you are makes a difference for the this keyword. When you are in sloppy mode, this will refer to the global object window. Global object global in Node.js. This is true even for functions declared inside another functions, in a local scope.

// Function example no.1: function in a global scope
// Declare a function.
function foo() {
  // Log the value of this
  console.log(this)
  console.log(this === window)
}

// Invoke foo() function.
foo()
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true


// Function example no.2: function in a local scope
// Declare a function.
function foo() {
  return function bar() {
    // Log the value of this
    console.log(this)
    console.log(this === window)
  }
}

// Invoke foo() and bar() functions.
foo()()
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true
Enter fullscreen mode Exit fullscreen mode

Strict mode

This will change if you switch your JavaScript code to strict mode. In a strict mode, the default value of this in a function is set to undefined.

// Strict mode example no.1: function in a global scope
// Switch to strict mode.
'use strict'

// Declare a function.
function foo() {
  // Log the value of this
  console.log(this)
  console.log(this === window)
}

// Invoke foo() function.
foo()
// Output:
// undefined
// false


// Strict mode example no.2: function in a local scope
// Switch to strict mode.
'use strict'

// Declare a function.
function foo() {
  return function bar() {
    // Log the value of this
    console.log(this)
    console.log(this === window)
  }
}

// Invoke foo() and bar() functions.
foo()()
// Output:
// undefined
// false
Enter fullscreen mode Exit fullscreen mode

Immediately Invoked Function Expression (IIFE)

The this keyword works in IIFEs like in regular functions. In a sloppy mode, this will refer to the global object window. If you switch to a strict the value of this will become undefined.

// IIFE example no.1: sloppy mode
// Declare IIFE.
(function() {
  console.log(this)
  console.log(this === window)
})()
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true


// IIFE example no.2: strict mode
// Switch to strict mode.
'use strict';

// Declare IIFE.
(function() {
  console.log(this)
  console.log(this === window)
})()
// Output:
// undefined
// false
Enter fullscreen mode Exit fullscreen mode

Function constructors

When you want to create new functions with similar behavior you can use function constructor. This allows you to create a blueprint you can then use for your functions. If you use function constructor remember one thing. The this keyword inside a constructor will refer to the instance of that constructor, not the constructor itself. This applies to both, sloppy and strict mode.

// Function constructors example no.1: sloppy mode
// Create function constructor Person.
function Person(name, age) {
  this.name = name
  this.age = age

  this.readPerson = function() {
    console.log(this)
  }
}

// Create joe instance.
const joe = new Person('Joe', 31)

// Create natalie instance.
const natalie = new Person('Natalie', 28)

// Invoke readPerson() method on joe instance.
joe.readPerson()
// Output:
// Person {
//   name: 'Joe',
//   age: 31,
//   readPerson: ƒ (),
//   __proto__: Person { constructor: ƒ Person() }
// }

// Invoke readPerson() method on natalie instance.
natalie.readPerson()
// Output:
// Person {
//   name: 'Natalie',
//   age: 28,
//   readPerson: ƒ (),
//   __proto__: Person { constructor: ƒ Person() }
// }


// Function constructors example no.2: strict mode
// Switch to strict mode.
'use strict'

// Create function constructor Person.
function Person(name, age) {
  this.name = name
  this.age = age

  this.readPerson = function() {
    console.log(this)
  }
}

// Create joe instance.
const joe = new Person('Joe', 31)

// Create natalie instance.
const natalie = new Person('Natalie', 28)

// Invoke readPerson() method on joe instance.
joe.readPerson()
// Output:
// Person {
//   name: 'Joe',
//   age: 31,
//   readPerson: ƒ (),
//   __proto__: Person { constructor: ƒ Person() }
// }

// Invoke readPerson() method on natalie instance.
natalie.readPerson()
// Output:
// Person {
//   name: 'Natalie',
//   age: 28,
//   readPerson: ƒ (),
//   __proto__: Person { constructor: ƒ Person() }
// }
Enter fullscreen mode Exit fullscreen mode

Objects and methods

When you use this keyword in an object method, the result can vary. What matters is if the method is a regular function or an arrow function. You will learn about this and arrow functions later. For now, let's focus on regular functions.

When you use this in an object method, it will refer to the object itself. This is for both, sloppy as well as strict mode.

// Object example no.1: sloppy mode
const myObj = {
  name: 'Jack',
  age: 30,
  readObj() {
    console.log(this)
  }
}

// Invoke the readObj() method on myObj.
myObj.readObj()
// Output:
// { name: 'Jack', age: 30, readObj: ƒ readObj() }


// Object example no.2: strict mode
// Switch to strict mode.
'use strict'

const myObj = {
  name: 'Jack',
  age: 30,
  readObj() {
    console.log(this)
  }
}

// Invoke the readObj() method on myObj.
myObj.readObj()
// Output:
// { name: 'Jack', age: 30, readObj: ƒ readObj() }
Enter fullscreen mode Exit fullscreen mode

Classes

JavaScript classes are a newer addition to JavaScript. They are definitely one of those more discussed features. Some developers like to use them and some not. If you like using them, or want to start using them, you will like what follows. When it comes to classes, the this keyword is very consistent and predictable.

It doesn't matter if you are in a sloppy mode or a strict mode. If you use this in a class it will refer to the class itself.

// Classes example no.1: with instantiation in sloppy mode (regular function, no binding)
// Declare a class with public property and method.
class Person {
  constructor(name) {
    this.name = name
  }

  sayHi() {
    console.log(this)
  }
}

// Instantiate the Person class.
const joshua = new Person('Joshua')

// Invoke sayHi() on "joshua" instance.
joshua.sayHi()
// Output:
// Person {name: "Joshua"}


// Classes example no.2: with instantiation in sloppy mode (arrow function)
// Declare a class with public property and method.
class Person {
  constructor(name) {
    this.name = name
  }

  sayHi = () => {
    console.log(this)
  }
}

// Instantiate the Person class.
const joshua = new Person('Joshua')

// Invoke sayHi() on "joshua" instance.
joshua.sayHi()
// Output:
// Person {name: "Joshua", sayHi: ƒ}


// Classes example no.3: with instantiation in strict mode (regular function, no binding)
// Switch to strict mode.
'use strict'

// Declare a class with public property and method.
class Person {
  constructor(name) {
    this.name = name
  }

  sayHi() {
    console.log(this)
  }
}

// Instantiate the Person class.
const joshua = new Person('Joshua')

// Invoke sayHi() on "joshua" instance.
joshua.sayHi()
// Output:
// Person {name: "Joshua"}


// Classes example no.4: with instantiation in strict mode (arrow function)
// Switch to strict mode.
'use strict'

// Declare a class with public property and method.
class Person {
  constructor(name) {
    this.name = name
  }

  sayHi = () => {
    console.log(this)
  }
}

// Instantiate the Person class.
const joshua = new Person('Joshua')

// Invoke sayHi() on "joshua" instance.
joshua.sayHi()
// Output:
// Person {
//   sayHi: ƒ (),
//   name: 'Joshua',
//   __proto__: Person { constructor: ƒ Person() }
// }


// Classes example no.5: without instantiation in sloppy mode (regular function, no binding)
// Declare a class with static property and method.
class Person {
  static name = 'Luke'
  static sayHi() {
    console.log(this)
    console.log(this === Person)
  }
}

// Invoke sayHi() method.
Person.sayHi()
// Output:
// class Person {
//   static name = 'Luke'
//   static sayHi() {
//     console.log(this)
//     console.log(this === Person)
//   }
// }
// true


// Classes example no.6: without instantiation in sloppy mode (arrow function)
// Declare a class with static property and method.
class Person {
  static name = 'Luke'
  static sayHi = () => {
    console.log(this)
    console.log(this === Person)
  }
}

// Invoke sayHi() method.
Person.sayHi()
// Output:
// class Person {
//   static name = 'Luke'
//   static sayHi = () => {
//     console.log(this)
//     console.log(this === Person)
//   }
// }
// true


// Classes example no.7: without instantiation in strict mode (regular function, no binding)
// Switch to strict mode.
'use strict'

// Declare a class with static property and method.
class Person {
  static name = 'Luke'
  static sayHi() {
    console.log(this)
    console.log(this === Person)
  }
}

// Invoke sayHi() method.
Person.sayHi()
// Output:
// class Person {
//   static name = 'Luke'
//   static sayHi() {
//     console.log(this)
//     console.log(this === Person)
//   }
// }
// true


// Classes example no.8: without instantiation in strict mode (arrow function)
// Switch to strict mode.
'use strict'

// Declare a class with static property and method.
class Person {
  static name = 'Luke'
  static sayHi = () => {
    console.log(this)
    console.log(this === Person)
  }
}

// Invoke sayHi() method.
Person.sayHi()
// Output:
// class Person {
//   static name = 'Luke'
//   static sayHi = () => {
//     console.log(this)
//     console.log(this === Person)
//   }
// }
// true
Enter fullscreen mode Exit fullscreen mode

Event listeners

When you use the this keyword with event listeners, it will refer to the HTML element to which you attach the event listener. If you attach event listener to a button, this will refer to that button element. That button will become value of this. If you attach event listener to global window object, this will refer to the global window object.

// Event listener example no.1: sloppy mode
// Find button in the DOM.
const btn = document.querySelector('.btn')

// Attach event listener to the button.
btn.addEventListener('click', function() {
  console.log(this)
  console.log(this === window)
})

// Output on click on the button:
// <button>Click me</button>
// false


// Arrow function example no.2: strict mode
// Switch to strict mode.
'use strict'

// Find button in the DOM.
const btn = document.querySelector('.btn')

// Attach event listener to the button.
btn.addEventListener('click', function() {
  console.log(this)
  console.log(this === window)
})

// Output on click on the button:
// <button>Click me</button>
// false


// Arrow function example no.3: event listener on window
// Attach event listener to the button.
window.addEventListener('click', function() {
  console.log(this)
  console.log(this === window)
})

// Output on click on the button:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true
Enter fullscreen mode Exit fullscreen mode

Arrow functions

With arrow functions, this keyword works differently than with regular functions. Arrow functions don't have their own this. When you use this in an arrow function it will inherit its value from its context. Context here is the context at which you defined that arrow function.

Global and local scope, this and arrow functions

If your arrow function is in a global scope, this will refer to the global object window. This is true for sloppy and strict mode. It is also true if the arrow function is inside a regular function, in a sloppy mode. If you are in a strict mode, and arrow function is inside a regular function, the value of this will be undefined.

// Arrow function example no.1: global function in a sloppy mode
// Declare an arrow function.
const foo = () => {
  // Log the value of this.
  console.log(this)
  console.log(this === window)
}

// Invoke foo() and bar() functions.
foo()
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true


// Arrow function example no.2: global function in a strict mode
// Switch to strict mode.
'use strict'

// Declare a function.
const foo = () => {
  // Log the value of this.
  console.log(this)
  console.log(this === window)
}

// Invoke foo() and bar() functions.
foo()
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true


// Arrow function example no.3: local function in a sloppy mode
// Declare a regular function.
function foo() {
  // Return an arrow function.
  return () => {
    // Log the value of this.
    console.log(this)
    console.log(this === window)
  }
}

// Invoke foo() and bar() functions.
foo()()
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true


// Arrow function example no.4: local function in a strict mode
// Switch to strict mode.
'use strict'

// Declare a regular function.
function foo() {
  // Return an arrow function.
  return () => {
    // Log the value of this.
    console.log(this)
    console.log(this === window)
  }
}

// Invoke foo() and bar() functions
foo()()
// Output:
// undefined
// false
Enter fullscreen mode Exit fullscreen mode

Arrow IIFEs

When you use arrow function to create Immediately Invoked Function Expression (IIFE) this will refer to global window object. This applies to both, sloppy as well as strict mode.

// Arrow IIFE example no.1: sloppy mode
// Declare arrow IIFE.
(() => {
  console.log(this)
  console.log(this === window)
})()
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true


// Arrow IIFE example no.2: strict mode
// Switch to strict mode.
'use strict';

// Declare arrow IIFE.
(() => {
  console.log(this)
  console.log(this === window)
})()
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true
Enter fullscreen mode Exit fullscreen mode

Objects, classes, this and arrow functions

If you declared your arrow function in an object, this will refer to global object window. In case of a class, it will refer to the class itself.

// Arrow function example no.5: object in sloppy mode
// Declare an object.
const obj = {
  name: 'Luke',
  sayHi: () => {
    console.log(this)
    console.log(this === window)
  }
}

obj.sayHi()
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true


// Arrow function example no.6: object in strict mode
// Switch to strict mode.
'use strict'

// Declare a function
const obj = {
  name: 'Luke',
  sayHi: () => {
    console.log(this)
    console.log(this === window)
  }
}

obj.sayHi()
// Output:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true


// Arrow function example no.7: class in sloppy mode
// Declare a class with static property and method.
class Person {
  static name = 'Luke'
  static sayHi = () => {
    console.log(this)
    console.log(this === Person)
  }
}

Person.sayHi()
// Output:
// Luke()
// true


// Arrow function example no.8: class in strict mode
// Switch to strict mode.
'use strict'

// Declare a class with static property and method.
class Person {
  static name = 'Luke'
  static sayHi = () => {
    console.log(this)
    console.log(this === Person)
  }
}

Person.sayHi()
// Output:
// Luke()
// true
Enter fullscreen mode Exit fullscreen mode

Event listeners, this and arrow functions

If you use arrow function as a callback for event listener, this will refer to global object window. This will happen in both, sloppy and also strict mode.

// Arrow function example no.9: event listener in sloppy mode
// Find button in the DOM.
const btn = document.querySelector('.btn')

// Attach event listener to the button.
btn.addEventListener('click', () => {
  console.log(this)
  console.log(this === window)
})

// Output on click on the button:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true


// Arrow function example no.10: event listener in strict mode
// Switch to strict mode.
'use strict'

// Find button in the DOM.
const btn = document.querySelector('.btn')

// Attach event listener to the button.
btn.addEventListener('click', () => {
  console.log(this)
  console.log(this === window)
})

// Output on click on the button:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true


// Arrow function example no.11: event listener on window
// Attach event listener to the button.
window.addEventListener('click', () => {
  console.log(this)
  console.log(this === window)
})

// Output on click on the button:
// Window {0: Window, 1: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
// true
Enter fullscreen mode Exit fullscreen mode

Conclusion: A quick guide to this keyword in JavaScript

The this keyword can be sometimes confusing and unpredictable. It is no wonder some JavaScript developers don't like to use it, and even discourage it. I hope that this tutorial helped you understand what this keyword refers to in specific contexts.

Top comments (2)

Collapse
 
diegolepore profile image
Diego Palacios Lepore • Edited

Great post! I liked your approach of explaining how the this binding works based on either sloppy or strict mode.

By the way, I also wrote an article about “this” a few months ago, here’s the link in case you want to take a look: dev.to/diegolepore/what-s-the-bind...

My main focus in this article is explaining the “this” binding based on “where” and “how” a function is invoke, and how answering these two questions will lead us to the binding of “this” to a specific object. Understanding this gives us the ability to take advantage of this feature in our programs. It’s really cool 🙂

Cheers! 👋🏻

Collapse
 
alexdevero profile image
Alex Devero

Thanks. About this. I wrote two articles about this topic as well:
blog.alexdevero.com/this-in-javasc...
blog.alexdevero.com/this-keyword-i...