Why 'this' in JavaScript Differs from Other OOP Languages

One of the most confusing aspects of JavaScript for me was understanding how the this keyword works. The this keyword in JavaScript behaves differently from OOP languages like C#, Java, or Python (where the equivalent is self) because of how this is determined. In those languages, this consistently refers to the current instance of the class or object. In JavaScript, however, this is more dynamic and its value depends on how and where a function is invoked. With that in mind, I decided to create a summary of the different scenarios where the behavior of this can vary. Here's the breakdown: 1. Global Context Non-Strict Mode: this refers to the global object (window in browsers, global in Node.js). console.log(this); // `window` or `global` Strict Mode: this is undefined. "use strict"; console.log(this); // undefined 2. Inside a Function Regular Function: In non-strict mode, this refers to the global object. In strict mode, this is undefined. function myFunction() { console.log(this); } myFunction(); // `window` (non-strict), undefined (strict) 3. As a Method of an Object When a function is called as a method of an object, this refers to the object that owns the method. const obj = { name: "JavaScript", greet() { console.log(this.name); // `this` refers to `obj` } }; obj.greet(); // Output: "JavaScript" 4. Arrow Functions Arrow functions do not have their own this. Instead, they inherit this from their lexical scope (the surrounding context). const obj = { name: "JavaScript", arrowFunc: () => { console.log(this.name); // `this` is inherited from the global scope } }; obj.arrowFunc(); // undefined (in browsers, `this` is `window`) function outer() { const arrow = () => console.log(this); arrow(); // Inherits `this` from `outer`'s context } outer(); 5. In a Constructor In a constructor function or class, this refers to the instance being created. class Person { constructor(name) { this.name = name; } greet() { console.log(`Hello, ${this.name}`); } } const person = new Person("Alice"); person.greet(); // Output: Hello, Alice 6. Explicit Binding (call, apply, bind) In JavaScript, functions are objects and have methods like call, apply, and bind, which let you explicitly set the value of this. call and apply: Invoke the function with a specified this value. Difference: call accepts arguments as a comma-separated list; apply takes an array. function greet(greeting) { console.log(`${greeting}, ${this.name}`); } const user = { name: "Alice" }; greet.call(user, "Hello"); // Output: Hello, Alice greet.apply(user, ["Hi"]); // Output: Hi, Alice bind: Returns a new function with this permanently bound to the specified value. const boundGreet = greet.bind(user); boundGreet("Hello"); // Output: Hello, Alice 7. In an Event Listener Regular Function: this refers to the element that triggered the event. const button = document.querySelector("button"); button.addEventListener("click", function () { console.log(this); // The button element }); Arrow Function: this inherits from the surrounding scope, not the element. button.addEventListener("click", () => { console.log(this); // `this` depends on where the arrow function is defined }); 8. Inside setTimeout or setInterval Regular Function: this defaults to the global object (window). setTimeout(function () { console.log(this); // `window` in browsers }, 1000); Arrow Function: Inherits this from its lexical scope. setTimeout(() => { console.log(this); // Inherits `this` from the surrounding context }, 1000); 9. In Classes Inside a class method, this refers to the class instance. class MyClass { constructor(name) { this.name = name; } sayName() { console.log(this.name); } } const obj = new MyClass("JavaScript"); obj.sayName(); // Output: JavaScript 10. Losing Context (Method Extraction) When a method is assigned to a variable or passed as a callback, this can lose its binding. const obj = { name: "JavaScript", greet() { console.log(this.name); } }; const greet = obj.greet; greet(); // undefined (`this` is no longer bound to `obj`) Solutions: Use bind: const boundGreet = obj.greet.bind(obj); boundGreet(); // Output: JavaScript Use an Arrow Function: const obj = { name: "JavaScript", greet: () => console.log(this.name) // `this` is inherited lexically }; obj.greet(); // Output depends on outer scope 11. In new Keyword Usage When you use new with a function

Jan 17, 2025 - 07:30
Why 'this' in JavaScript Differs from Other OOP Languages

One of the most confusing aspects of JavaScript for me was understanding how the this keyword works.

The this keyword in JavaScript behaves differently from OOP languages like C#, Java, or Python (where the equivalent is self) because of how this is determined. In those languages, this consistently refers to the current instance of the class or object. In JavaScript, however, this is more dynamic and its value depends on how and where a function is invoked.

With that in mind, I decided to create a summary of the different scenarios where the behavior of this can vary. Here's the breakdown:

1. Global Context

Non-Strict Mode:

  • this refers to the global object (window in browsers, global in Node.js).
console.log(this); // `window` or `global`

Strict Mode:

  • this is undefined.
"use strict";
console.log(this); // undefined

2. Inside a Function

Regular Function:

  • In non-strict mode, this refers to the global object.
  • In strict mode, this is undefined.
function myFunction() {
    console.log(this);
}
myFunction(); // `window` (non-strict), undefined (strict)

3. As a Method of an Object

  • When a function is called as a method of an object, this refers to the object that owns the method.
const obj = {
    name: "JavaScript",
    greet() {
        console.log(this.name); // `this` refers to `obj`
    }
};
obj.greet(); // Output: "JavaScript"

4. Arrow Functions

  • Arrow functions do not have their own this. Instead, they inherit this from their lexical scope (the surrounding context).
const obj = {
    name: "JavaScript",
    arrowFunc: () => {
        console.log(this.name); // `this` is inherited from the global scope
    }
};
obj.arrowFunc(); // undefined (in browsers, `this` is `window`)

function outer() {
    const arrow = () => console.log(this);
    arrow(); // Inherits `this` from `outer`'s context
}
outer();

5. In a Constructor

  • In a constructor function or class, this refers to the instance being created.
class Person {
    constructor(name) {
        this.name = name;
    }
    greet() {
        console.log(`Hello, ${this.name}`);
    }
}

const person = new Person("Alice");
person.greet(); // Output: Hello, Alice

6. Explicit Binding (call, apply, bind)

In JavaScript, functions are objects and have methods like call, apply, and bind, which let you explicitly set the value of this.

call and apply:

  • Invoke the function with a specified this value.
  • Difference: call accepts arguments as a comma-separated list; apply takes an array.
function greet(greeting) {
    console.log(`${greeting}, ${this.name}`);
}

const user = { name: "Alice" };
greet.call(user, "Hello"); // Output: Hello, Alice
greet.apply(user, ["Hi"]); // Output: Hi, Alice

bind:

  • Returns a new function with this permanently bound to the specified value.
const boundGreet = greet.bind(user);
boundGreet("Hello"); // Output: Hello, Alice

7. In an Event Listener

Regular Function:

  • this refers to the element that triggered the event.
const button = document.querySelector("button");
button.addEventListener("click", function () {
    console.log(this); // The button element
});

Arrow Function:

  • this inherits from the surrounding scope, not the element.
button.addEventListener("click", () => {
    console.log(this); // `this` depends on where the arrow function is defined
});

8. Inside setTimeout or setInterval

Regular Function:

  • this defaults to the global object (window).
setTimeout(function () {
    console.log(this); // `window` in browsers
}, 1000);

Arrow Function:

  • Inherits this from its lexical scope.
setTimeout(() => {
    console.log(this); // Inherits `this` from the surrounding context
}, 1000);

9. In Classes

  • Inside a class method, this refers to the class instance.
class MyClass {
    constructor(name) {
        this.name = name;
    }

    sayName() {
        console.log(this.name);
    }
}

const obj = new MyClass("JavaScript");
obj.sayName(); // Output: JavaScript

10. Losing Context (Method Extraction)

When a method is assigned to a variable or passed as a callback, this can lose its binding.

const obj = {
    name: "JavaScript",
    greet() {
        console.log(this.name);
    }
};

const greet = obj.greet;
greet(); // undefined (`this` is no longer bound to `obj`)

Solutions:

  1. Use bind:
   const boundGreet = obj.greet.bind(obj);
   boundGreet(); // Output: JavaScript
  1. Use an Arrow Function:
   const obj = {
       name: "JavaScript",
       greet: () => console.log(this.name) // `this` is inherited lexically
   };
   obj.greet(); // Output depends on outer scope

11. In new Keyword Usage

When you use new with a function or class, this refers to the newly created object.

function MyConstructor() {
    this.name = "New Object";
}
const instance = new MyConstructor();
console.log(instance.name); // Output: New Object

Summary

Context this Refers To
Global (non-strict) Global object (window/global).
Global (strict) undefined.
Object Method The object that owns the method.
Arrow Function Inherits this from the enclosing context.
Constructor/Class The instance being created.
call, apply, bind Explicitly defined value.
Event Listener The element that triggered the event.
setTimeout/Interval Global object or inherited context (arrow).
new Keyword The newly created object.