JavaScript this Keyword
The this keyword is one of the most confusing parts of JavaScript. Its value depends on how a function is called, not where it's defined. Master this to write more flexible and powerful code.
Try It Yourself
Explore how this behaves in different contexts:
// The 'this' keyword in different contexts
const person = {
name: "Alice",
// Method - 'this' is the object
greet() {
console.log("Method this.name:", this.name);
},
// Arrow function - 'this' is inherited (global)
greetArrow: () => {
console.log("Arrow this.name:", this.name);
},
// Callback issue and fix
delayedGreet() {
// Problem: regular function loses 'this'
setTimeout(function() {
// console.log(this.name); // undefined!
}, 100);
// Solution: arrow function preserves 'this'
setTimeout(() => {
console.log("Delayed (arrow):", this.name);
}, 100);
}
};
console.log("--- Method calls ---");
person.greet(); // "Alice"
person.greetArrow(); // undefined (arrow inherits global this)
console.log("\n--- Delayed call ---");
person.delayedGreet();this in Global Context
In the global scope, this refers to the global object:
// In global scope, 'this' refers to the global object
console.log(this === globalThis); // true (in modern JS)
// In browsers: this === window
// In Node.js: this === global
function showThis() {
console.log(this);
}
showThis(); // Window (browser) or global (Node)
// In strict mode, 'this' is undefined in regular functions
"use strict";
function strictThis() {
console.log(this); // undefined
}
strictThis();this in Methods
When a function is called as a method, this refers to the object that owns the method:
// In a method, 'this' refers to the object
const user = {
name: "Alice",
age: 25,
introduce() {
console.log("Hi, I'm " + this.name);
console.log("I'm " + this.age + " years old");
},
getInfo() {
return {
name: this.name,
age: this.age
};
}
};
user.introduce();
// "Hi, I'm Alice"
// "I'm 25 years old"
console.log(user.getInfo());
// { name: "Alice", age: 25 }Implicit Binding
The object "to the left of the dot" becomes this:
// Implicit binding: object to the left of the dot
const person = {
name: "Alice",
greet() {
console.log("Hello, " + this.name);
}
};
person.greet(); // "Hello, Alice" - this = person
// Nested objects: look at the immediate caller
const company = {
name: "TechCorp",
employee: {
name: "Bob",
greet() {
console.log("Hello, " + this.name);
}
}
};
company.employee.greet(); // "Hello, Bob" - this = employee, not companyLosing this Context
It's easy to lose the this context when passing methods around:
// Common ways to lose 'this' context
const person = {
name: "Alice",
greet() {
console.log("Hello, " + this.name);
}
};
// 1. Assigning method to a variable
const greetFunc = person.greet;
greetFunc(); // "Hello, undefined" - 'this' is global/undefined
// 2. Passing as callback
function callFunc(fn) {
fn();
}
callFunc(person.greet); // "Hello, undefined"
// 3. Using in event handlers (conceptual)
// button.onclick = person.greet; // 'this' would be button, not person
// 4. Using in setTimeout
setTimeout(person.greet, 100); // "Hello, undefined"Explicit Binding: call, apply, bind
You can explicitly set this using these methods:
// Explicit binding with call, apply, bind
function greet(greeting, punctuation) {
console.log(greeting + ", " + this.name + punctuation);
}
const person = { name: "Alice" };
// call() - invoke with this, pass args individually
greet.call(person, "Hello", "!");
// "Hello, Alice!"
// apply() - invoke with this, pass args as array
greet.apply(person, ["Hi", "?"]);
// "Hi, Alice?"
// bind() - returns new function with this bound
const greetAlice = greet.bind(person);
greetAlice("Hey", ".");
// "Hey, Alice."
// bind() with partial application
const sayHi = greet.bind(person, "Hi");
sayHi("!!!");
// "Hi, Alice!!!"new Binding
When using new, this refers to the newly created object:
// 'new' binding: this refers to the new object
function Person(name, age) {
// 'this' is automatically created as {}
this.name = name;
this.age = age;
// 'this' is automatically returned
}
const alice = new Person("Alice", 25);
console.log(alice.name); // "Alice"
console.log(alice.age); // 25
// What 'new' does:
// 1. Creates a new empty object
// 2. Sets 'this' to that object
// 3. Executes the constructor
// 4. Returns 'this' (unless you return a different object)Arrow Functions and this
Arrow functions don't have their own this. They inherit it from the enclosing scope:
// Arrow functions: 'this' is lexically inherited
const person = {
name: "Alice",
// Regular method
regularMethod() {
console.log("Regular:", this.name); // "Alice"
},
// Arrow function as property - inherits global 'this'
arrowProperty: () => {
console.log("Arrow property:", this.name); // undefined
},
// Arrow in method - inherits method's 'this'
methodWithArrow() {
const inner = () => {
console.log("Arrow in method:", this.name); // "Alice"
};
inner();
}
};
person.regularMethod(); // "Alice"
person.arrowProperty(); // undefined
person.methodWithArrow(); // "Alice"Fixing this in Callbacks
Multiple ways to preserve this in callback functions:
// Fixing 'this' in callbacks
const team = {
name: "Development",
members: ["Alice", "Bob", "Carol"],
// PROBLEM: regular function callback loses 'this'
showMembersBroken() {
this.members.forEach(function(member) {
// console.log(this.name + ": " + member); // Error! this is undefined
});
},
// SOLUTION 1: Arrow function (most common)
showMembersArrow() {
this.members.forEach((member) => {
console.log(this.name + ": " + member);
});
},
// SOLUTION 2: bind()
showMembersBind() {
this.members.forEach(function(member) {
console.log(this.name + ": " + member);
}.bind(this));
},
// SOLUTION 3: thisArg parameter
showMembersThisArg() {
this.members.forEach(function(member) {
console.log(this.name + ": " + member);
}, this); // Second argument sets 'this'
},
// SOLUTION 4: Save reference (old pattern)
showMembersSelf() {
const self = this;
this.members.forEach(function(member) {
console.log(self.name + ": " + member);
});
}
};
team.showMembersArrow();Binding Precedence
When multiple rules could apply, JavaScript uses this precedence:
// Binding precedence (highest to lowest)
function identify() {
console.log("Name:", this.name);
}
const person = { name: "Alice" };
const another = { name: "Bob" };
// 1. new binding (highest priority)
function Person(name) {
this.name = name;
}
const p = new Person("Carol");
console.log(p.name); // "Carol"
// 2. Explicit binding (call/apply/bind)
identify.call(person); // "Alice"
// 3. Implicit binding (method call)
const obj = { name: "Dave", identify };
obj.identify(); // "Dave"
// 4. Default binding (lowest - global or undefined)
identify(); // undefined (strict) or global name
// new > explicit > implicit > defaultthis Quick Reference
| Context | this Value |
|---|---|
| Global (non-strict) | globalThis / window |
| Global (strict) | undefined |
| Object method | The object (left of dot) |
| call/apply/bind | Explicitly specified |
| new Constructor() | The new instance |
| Arrow function | Inherited from enclosing scope |
| Event handler | The element (DOM) |
this in ES6 Classes
Classes have the same this behavior, with some convenient patterns:
// 'this' in ES6 classes
class Counter {
constructor() {
this.count = 0;
// Bind method in constructor (common pattern)
this.increment = this.increment.bind(this);
}
increment() {
this.count++;
console.log("Count:", this.count);
}
// Arrow function as class field (auto-bound)
decrement = () => {
this.count--;
console.log("Count:", this.count);
}
}
const counter = new Counter();
// Both work when passed as callbacks
const inc = counter.increment;
const dec = counter.decrement;
inc(); // "Count: 1" - works (bound in constructor)
dec(); // "Count: 0" - works (arrow function)Test Your Knowledge
JavaScript this Keyword Quiz
5 questionsWhat is "this" in a regular function called without an object?
Why don't arrow functions have their own "this"?
What does bind() return?
What is the binding precedence order (highest first)?
How do you fix "this" in a forEach callback?
Coding Challenge
Create a Timer class with start, stop, reset, and getTime methods. The timer should increment every second. Make sure this works correctly in the setInterval callback!
// Challenge: Create a Timer class
// Requirements:
// - start() begins counting seconds
// - stop() pauses the timer
// - reset() sets count back to 0
// - getTime() returns current count
// The timer should increment every second and log the time
// Make sure 'this' works correctly in the setInterval callback!
class Timer {
constructor() {
// Your code here
}
start() {
// Your code here
}
stop() {
// Your code here
}
reset() {
// Your code here
}
getTime() {
// Your code here
}
}
// Test your implementation
const timer = new Timer();
console.log("Starting timer...");
timer.start();
// After 3 seconds, stop and show time
setTimeout(() => {
timer.stop();
console.log("Stopped at:", timer.getTime(), "seconds");
timer.reset();
console.log("After reset:", timer.getTime(), "seconds");
}, 3500);Summary
- this is determined by how a function is called, not where it's defined
- In methods, this is the object to the left of the dot
- In regular functions, this is global (or undefined in strict mode)
- Arrow functions inherit this from their enclosing scope
- Use call/apply to invoke with explicit this, bind to create a bound function
- Precedence: new > explicit > implicit > default
- Fix callbacks with arrow functions or bind()