The this Keyword
The this keyword refers to the execution context of a function call. Its value is not determined by where a function is written — it depends entirely on how the function is invoked. Misunderstanding this is one of the most common sources of bugs in JavaScript.
The Four Binding Rules (Plus Arrow Functions)
| Rule | Invocation | this value |
|---|---|---|
| Default | fn() |
globalThis (or undefined in strict mode) |
| Implicit | obj.fn() |
obj |
| Explicit | fn.call(obj) |
specified object |
new |
new Fn() |
newly created instance |
| Arrow | () => {} |
lexical this from enclosing scope |
Default Binding
Calling a standalone function uses default binding:
function showThis() {
console.log(this);
}
showThis(); // window (browser, non-strict) or undefined (strict mode)
In strict mode, plain function calls set this to undefined — preventing accidental global access.
Implicit Binding
When a function is accessed as a property of an object and called with dot notation, this is that object:
const user = {
name: 'Alice',
greet() {
console.log(`Hello, ${this.name}`);
}
};
user.greet(); // 'Hello, Alice' — this === user
Lost Binding (The Classic Bug)
Extracting a method loses the object context:
const greet = user.greet;
greet(); // 'Hello, undefined' — this is NOT user
// Common in callbacks:
setTimeout(user.greet, 1000); // also broken
Fixes:
// 1. Bind explicitly
setTimeout(user.greet.bind(user), 1000);
// 2. Wrapper function
setTimeout(() => user.greet(), 1000);
// 3. Arrow method (inherits lexical this — see below)
const timer = {
count: 0,
start() {
setInterval(() => {
this.count++; // this === timer
}, 1000);
}
};
Explicit Binding: call, apply, bind
Force this to a specific value:
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const user = { name: 'Bob' };
greet.call(user, 'Hi', '!'); // 'Hi, Bob!'
greet.apply(user, ['Hello', '.']); // 'Hello, Bob.'
const boundGreet = greet.bind(user);
boundGreet('Hey', '?'); // 'Hey, Bob?'
| Method | Arguments | Returns |
|---|---|---|
call |
Comma-separated | Immediate invocation |
apply |
Array | Immediate invocation |
bind |
Comma-separated | New function with fixed this |
new Binding
When a function is invoked with new, this is the newly created object:
function Person(name) {
this.name = name;
}
const p = new Person('Charlie');
console.log(p.name); // 'Charlie'
Arrow Functions and Lexical this
Arrow functions do not have their own this. They inherit this from the enclosing scope at write time:
const counter = {
count: 0,
start() {
setInterval(() => {
this.count++; // this === counter (lexical)
console.log(this.count);
}, 1000);
}
};
// Broken with regular function:
const broken = {
count: 0,
start() {
setInterval(function() {
this.count++; // this === window or undefined
}, 1000);
}
};
Do not use arrow functions as object methods when you need this to refer to the object:
const bad = {
name: 'Dave',
greet: () => console.log(this.name) // this is outer scope, not bad
};
this in Event Handlers
button.addEventListener('click', function() {
console.log(this); // the button element
this.classList.toggle('active');
});
button.addEventListener('click', () => {
console.log(this); // this from enclosing scope (NOT the button)
});
Use regular functions when you need the event target as this.
this in Classes
Class methods use implicit binding — this refers to the instance:
class Calculator {
constructor(value = 0) {
this.value = value;
}
add(n) {
this.value += n;
return this; // enable chaining
}
getResult() {
return this.value;
}
}
const calc = new Calculator(10);
calc.add(5).add(3);
console.log(calc.getResult()); // 18
Best Practices
- Use arrow functions for callbacks inside methods when you need the outer
this. - Use
.bind()or wrapper functions when passing methods as callbacks. - Avoid storing unbound method references — bind in the constructor or use arrow class fields.
- In strict mode, assume plain functions have
this === undefined. - Prefer class syntax for object-oriented patterns —
thisbehavior is more predictable.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
this is undefined in method |
Method passed as callback without binding | .bind(), arrow wrapper, or class field arrow |
this is window unexpectedly |
Non-strict plain function call | Enable strict mode; check invocation style |
Arrow method has wrong this |
Arrow functions ignore object context | Use regular method syntax |
this changes between calls |
Different invocation styles | Trace how the function is called |
Understanding this binding rules — default, implicit, explicit, new, and lexical — is essential for object-oriented patterns, event handlers, and any code that passes functions as callbacks.