Skip to main content

The 'this' Keyword Explained

In JavaScript, this is a keyword whose value is determined dynamically at call time (how and where a function is invoked), rather than where the function is defined. The only exception is arrow functions, which use lexical binding.

The 5 Rules of this Binding

To determine what this refers to in any function call, evaluate invocation against these 5 precedence rules:

Binding Precedence Hierarchy (Highest to Lowest)
┌─────────────────────────────────────────┐
│ 1. 'new' Binding │
│ 2. Explicit Binding (call, apply, bind) │
│ 3. Implicit Binding (Context Object) │
│ 4. Default / Global Binding │
│ 5. Lexical Binding (Arrow Functions) │
└─────────────────────────────────────────┘

1. Implicit Binding (Object Context)

When a function is invoked as a method of an object (using dot notation), this points to the object preceding the dot:

implicit-binding.js
const user = {
name: "Alex",
greet() {
return `Hello, my name is ${this.name}`;
}
};

console.log(user.greet()); // "Hello, my name is Alex"

Implicit Binding Loss

Assigning a method to a separate variable strips its object context:

implicit-binding-loss.js
const unboundGreet = user.greet;
console.log(unboundGreet()); // "Hello, my name is undefined" (or throws in strict mode)

2. Explicit Binding (call, apply, bind)

You can explicitly force a function to execute with a specific this context using JavaScript's built-in prototype methods:

MethodInvocationArguments FormatExecution
call()DirectComma-separated (obj, arg1, arg2)Executes immediately
apply()DirectArray of arguments (obj, [arg1, arg2])Executes immediately
bind()IndirectComma-separated (obj, arg1, arg2)Returns a new bound function
function updateProfile(role, location) {
this.role = role;
this.location = location;
return `${this.name} is a ${this.role} in ${this.location}`;
}

const person = { name: "Jordan" };

// 1. call
console.log(updateProfile.call(person, "Lead Architect", "Berlin"));

// 2. apply
console.log(updateProfile.apply(person, ["Principal Engineer", "Remote"]));

// 3. bind
const boundFn = updateProfile.bind(person, "DevOps Manager", "Tokyo");
console.log(boundFn());

3. new Binding (Constructor Invocation)

When a function is called with the new keyword:

  1. A brand new empty object {} is created.
  2. The object is linked to the function's prototype ([[Prototype]]).
  3. this inside the function is bound to that newly created object.
  4. The function implicitly returns the object (unless a different object is returned explicitly).
new-binding.js
function Developer(name, language) {
this.name = name;
this.language = language;
}

const dev = new Developer("Sam", "JavaScript");
console.log(dev.name); // "Sam"

4. Default / Global Binding

When a standalone function is called without any context object:

  • In Non-Strict Mode: this defaults to the global object (window in browsers, global in Node.js).
  • In Strict Mode ("use strict"): this evaluates to undefined.
default-binding.js
function checkContext() {
"use strict";
return this;
}

console.log(checkContext()); // undefined

5. Lexical this (Arrow Functions)

Arrow functions do not have their own this. They capture the this value from their enclosing execution scope at the time they are created:

arrow-function-this.js
const company = {
name: "CodeHarborHub",
getBrand: function () {
return `Company: ${this.name}`;
},
getBrandArrow: () => {
return `Company: ${this.name}`; // 'this' is lexically bound to the outer scope (global)
}
};
const timer = {
seconds: 0,
start() {
// Arrow function lexically captures 'this' from start() method context
setInterval(() => {
this.seconds++;
console.log(`Elapsed: ${this.seconds}s`);
}, 1000);
}
};

Interactive Playground: this Binding Lab

Test explicit binding, loss of implicit context, and arrow function behaviors live:

This Binding Playground
JavaScript Runtime
Console Output

Best Practices

  1. Use Arrow Functions for Callbacks: Preserve outer this inside event listeners, timers, and array methods without needing const self = this or explicit .bind(this).
  2. Avoid Arrow Functions for Object Methods: Defining object methods with arrow functions will bind this to the outer global scope instead of the object itself.
  3. Always Enable Strict Mode: Prevent accidental global variable mutations caused by default this binding in standalone functions.

Knowledge Check

Exercise Requirements:

Fix the bugs in the object below so getDetails correctly logs the user's details without throwing TypeError or logging undefined.

knowledge-check.js
const userProfile = {
username: "CodeNinja",
skills: ["JS", "TS", "React"],
getDetails: () => {
return `${this.username} knows ${this.skills.join(", ")}`;
}
};

solution.js
const userProfile = {
username: "CodeNinja",
skills: ["JS", "TS", "React"],
// Replace arrow function with standard method definition
getDetails() {
return `${this.username} knows ${this.skills.join(", ")}`;
}
};

console.log(userProfile.getDetails());
// Output: "CodeNinja knows JS, TS, React"

Phase 03 Complete!

Congratulations! You have completed Phase 03: Deep Dive Core. Proceed to Phase 04: Asynchronous JavaScript to master the event loop, promises, and async/await!