Understanding the `this` Keyword in JavaScript

Understanding this in JavaScript

By Dennis Wilke · March 18, 2025

JavaScript’s `this` keyword often confuses developers because its value changes depending on how and where a function is called. Let’s break down its behavior, rules, and best practices to avoid common pitfalls.

Join the 2.000+ members who have already signed up.

What is `this`?

`this` refers to the execution context of a function. Its value isn’t fixed – it’s determined when the function is invoked. Sounds complicated? No worries it isn’t, it’s based on these simple rules.

Rule 1: Global Context

Outside any function, `this` refers to the global object (`window` in browsers, `global` in Node.js).

console.log(this); // In a browser: Window {...}

⚠️ ATTENTION: In strict mode (’use strict’;), global`this` is `undefined`

‘use strict’;
console.log(this); // undefined (in global scope)

Rule 2: Function Context

In a standalone function, `this` depends on how the function is called:

function sayHello() { 
console.log(this); /* Window (or global object) */ 
} 
sayHello(); // Called in global scope

Using strict mode:

'use strict'; 
function sayHello() { 
  console.log(this); // undefined
} 
sayHello();

Rule 3: Object Context

When a function is a method of an object, `this` refers to the object itself:

const user = { 
  name: "Alice", 
  greet() {
    console.log(`Hello, ${this.name}!`); 
  } 
}; 

user.greet(); // "Hello, Alice!"

Pitfall: Losing Context ⚠️
If you extract the method, `this` becomes disconnected:

const greet = user.greet; 
greet(); // "Hello, undefined!" (this refers to global/undefined)

FIX: Use `bind` to lock the context:

const boundGreet = user.greet.bind(user);
boundGreet(); // "Hello, Alice!"

Rule 4: Arrow Functions

Arrow functions do not have their own `this`. They inherit `this` from the surrounding lexical scope:

const user = { 
name: "Bob", 
greet: () => {
  console.log(this.name); // Inherits global/window `this` 
  } 
}; 
user.greet(); // undefined (if strict mode) or ""

Better for Callbacks:

const button = document.querySelector(‘button’);
button.addEventListener(‘click’, function() {
  console.log(this); // Refers to the button element 
});

// Arrow function would inherit `this` from outer scope (e.g., window): 
button.addEventListener('click', () => {
  console.log(this); // Not the button!
});

Rule 5: Constructor Functions (Classes)

When using `new`, `this` refers to the newly created instance:

function Person(name) {
  this.name = name;
} 
const alice = new Person('Alice'); 
console.log(alice.name); // "Alice"

In Classes:

class User { 
  constructor(name) {
    this.name = name;
  }                          
  greet() { 
    console.log(`Hi, ${this.name}!`); 
  } 
} 
const bob = new User('Bob'); 
bob.greet(); // "Hi, Bob!"

Rule 6: Explicit Binding

Force `this` to a specific object using `call`, `apply`, or `bind`:

function describe(lang) { 
  console.log(`${this.name} codes in ${lang}`); 
} 

const dev = { name: "Charlie" };
describe.call(dev, ‘JavaScript’); // "Charlie codes in JavaScript" 

describe.apply(dev, [‘Python’]); // "Charlie codes in Python" 
const boundDescribe = describe.bind(dev); 

boundDescribe('Ruby'); // "Charlie codes in Ruby"

Common Mistakes & Fixes.

1. Nested Functions loosing `this`

const timer = { 
  seconds: 10, 
  start() {
    setInterval(function() { 
      console.log(this.seconds--); // `this` is global here and don’t points to 
      // the timer object anymore 
    }, 1000); 
  } 
}; 
timer.start(); // NaN (this.seconds is undefined)

Fix: Use an arrow function or `bind`:

// Arrow function (inherits `this` from `start`):
setInterval(() => { 
  console.log(this.seconds--); 
}, 1000); 
// Or bind: 
setInterval(function() {
  console.log(this.seconds--); 
}.bind(this), 1000); 
// binds the timer object context to the function

2. Event Handlers
In DOM event handlers, `this` refers to the element triggering the event:

const timer = { 
  seconds: 10, 
  start() {
    setInterval(function() { 
      console.log(this.seconds--);
      // `this` is global here and don’t points to 
      // the timer object anymore 
    }, 1000); 
  } 
}; 
timer.start(); // NaN (this.seconds is undefined)

Key Takeaways & Conclusion

So what’s the most important lesson of all this?

  1. `this` is determined by how a function is called, not where it’s defined.
  2. Use arrow functions to preserve lexical `this` (e.g., in callbacks).
  3. Explicitly set `this` with `bind`, `call`, or `apply` when needed.
  4. Avoid using `this` in standalone functions – it’s often a source of bugs.

Conclusion

How to use the this object can be tricky due to its dependencies within its context, but the best way to improve your understanding of the `this` keyword is to play around with it. Therefore use the code snippets to play around and see it in action. – Cheers