The this Keyword
How JavaScript determines what this refers to based on how a function is called
Overview
Key Points
- β’`this` is determined at call time, not at define time β the same function can have a different `this` on each call
- β’Implicit binding: `obj.method()` sets `this` to `obj`; the object before the dot wins
- β’Default binding: a standalone function call sets `this` to the global object (sloppy) or `undefined` (strict mode)
- β’Arrow functions have no own `this` β they inherit `this` from the enclosing lexical scope
- β’`call(thisArg, arg1, arg2)` and `apply(thisArg, [args])` invoke a function with an explicit `this`
- β’`bind(thisArg)` returns a new permanently-bound function without calling it immediately
- β’`new SomeFunction()` creates a fresh object, sets `this` to it inside the constructor, and returns it
Examples
Implicit binding β this is the object before the dot
const user = {
name: 'Alice',
greet() {
console.log('Hello, I am ' + this.name);
},
};
user.greet(); // 'Hello, I am Alice'
// this === user because greet() was called on userLosing this β assigning a method to a variable strips the object context
const user = {
name: 'Alice',
greet() {
console.log('Hello, I am ' + this.name);
},
};
const fn = user.greet;
fn(); // 'Hello, I am undefined' (or error in strict mode)
// this is now the global object, not userThis is the most common source of this-related bugs β extracting a method and calling it later.
Arrow function captures this from enclosing scope β fixes the callback bug
class Timer {
constructor() {
this.seconds = 0;
}
start() {
// Arrow function inherits this from start()'s scope (the Timer instance)
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
}
const t = new Timer();
t.start(); // logs 1, 2, 3...
// If we used function() instead of () =>, this.seconds would be undefinedcall, apply, and bind β setting this explicitly
function introduce(greeting, punctuation) {
console.log(greeting + ', I am ' + this.name + punctuation);
}
const alice = { name: 'Alice' };
const bob = { name: 'Bob' };
introduce.call(alice, 'Hi', '!'); // 'Hi, I am Alice!'
introduce.apply(bob, ['Hey', '?']); // 'Hey, I am Bob?'
const greetAlice = introduce.bind(alice, 'Hello');
greetAlice('.'); // 'Hello, I am Alice.'
// bind returns a new function β useful for event handlersnew binding β this is the freshly created instance
function Person(name, age) {
this.name = name; // this is the new object
this.age = age;
}
Person.prototype.describe = function () {
return `${this.name} is ${this.age} years old`;
};
const alice = new Person('Alice', 30);
console.log(alice.describe()); // 'Alice is 30 years old'
// new creates an empty object, sets this to it, runs the function, returns the objectCommon Mistakes
- Passing a method as a callback without binding it β the callback fires in the wrong context and this.property is undefined
- Using an arrow function as an object method expecting this to be the object β arrow functions capture this from outside the object literal, usually the global scope or undefined
- Forgetting that the rule is "object before the dot at call time" β even if greet lives on user, calling const g = user.greet; g() removes the implicit binding
- Expecting this inside a nested regular function to still refer to the outer object β only arrow functions share the outer this; regular nested functions get their own binding
Note
When in doubt, ask yourself: what is to the left of the dot when this function is actually called? That object is `this`. If nothing is to the left of the dot, default binding applies. Arrow functions skip this question entirely β they always answer "whatever `this` was where I was written."
Exercises
Write JavaScript code to solve each exercise. Run it and check your output.
Completed: 0 / 6 exercises
Knowledge Check
Question 1 of 4