this is one of the most talked-about words in JavaScript, because its value isn't fixed. Inside a function, this refers to the object the function was called on, and that's decided at the moment of the call, not when the function is written. The same function can see a different this each time it runs.
Most of the time it does what you'd expect. In user.greet(), this is user. Problems start when a method is passed somewhere else, for example as a callback to setTimeout or addEventListener. The function gets called without its object, this is lost, and you see errors like "Cannot read properties of undefined".
In this lesson you'll learn the simple rules that decide this, why arrow functions behave differently, and how the three function methods call(), apply() and bind() let you choose this yourself. These ideas matter for objects, classes and event handling, and they're another interview favourite.
The rules for this
| How the function is called | this is… |
|---|---|
As a method: obj.method() | obj, the object before the dot |
As a plain function: fn() | undefined in strict mode and modules, window otherwise |
With new: new User() | The newly created object |
With call, apply or bind | Whatever you pass as the first argument |
| As a DOM event handler (normal function) | The element the listener is attached to |
| Arrow function | Not its own: it uses this from the surrounding code |
The quick test: look at the call, not the definition. Whatever is to the left of the dot when the function is called is usually this.
Losing this
const timer = {
seconds: 10,
report() { console.log(this.seconds); },
};
timer.report(); // 10
const fn = timer.report;
fn(); // undefined: called without an object
setTimeout(timer.report, 0); // same problemCopying a method into a variable or passing it as a callback separates it from its object. When it's later called as a plain function, this is no longer timer. This is the single most common this bug.
Arrow functions and this
Arrow functions don't have their own this. They use the this of the code around them, fixed when they're created. That makes them perfect for callbacks inside methods:
const counter = {
count: 0,
start() {
setInterval(() => {
this.count++; // this is still counter
}, 1000);
},
};But it also means you shouldn't use an arrow function as a method: { name: "A", hi: () => this.name } won't see the object, because there's no surrounding method to borrow this from.
call, apply and bind
| Method | What it does | Example |
|---|---|---|
fn.call(obj, a, b) | Calls fn now with this = obj, arguments listed | greet.call(user, "Hi") |
fn.apply(obj, [a, b]) | Calls fn now, arguments in an array | Math.max.apply(null, nums) |
fn.bind(obj, a) | Returns a new function with this fixed to obj | const report = timer.report.bind(timer) |
bind is the one you'll use most, because it fixes the lost-this problem for callbacks: setTimeout(timer.report.bind(timer), 0). Nowadays apply is rarely needed, since the spread syntax Math.max(...nums) does the same job.
JavaScript this Keyword Syntax
this, call, apply and bind
const user = { name: "Ana", hi() { return `Hi, ${this.name}`; } };
user.hi(); // "Hi, Ana"
function intro(greeting) { return `${greeting}, ${this.name}`; }
intro.call(user, "Hello"); // "Hello, Ana"
intro.apply(user, ["Hey"]); // "Hey, Ana"
const bound = intro.bind(user);
bound("Hiya"); // "Hiya, Ana"JavaScript this Keyword Summary
thisis decided by how a function is called, not where it's written.- In
obj.method(),thisisobj; in a plain call it'sundefinedin strict mode. - Passing a method as a callback loses
this. - Arrow functions take
thisfrom the surrounding code, so use them for callbacks inside methods. callandapplyrun a function with a chosenthis;bindreturns a new function withthisfixed.
JavaScript this Keyword Examples
Each example runs in the page. Edit the code and press Run to see what changes.
Methods and lost this
const player = {
name: "Kai",
score: 40,
describe() {
return `${this?.name} has ${this?.score} points`;
},
};
console.log(player.describe());
const loose = player.describe;
console.log("Lost this:", loose());
const fixed = player.describe.bind(player);
console.log("Bound:", fixed());Line-by-line explanation
- Lines 4 to 5
thisrefers to whatever object the method is called on. - Line 9Called on
player, sothisisplayer. - Lines 11 to 12Called on its own,
thisis no longerplayer(it'sundefinedin strict mode, or the globalwindowotherwise), so the score is missing. - Lines 14 to 15
bindcreates a version wherethisis alwaysplayer.
Arrow functions inside methods
const quiz = {
title: "Capitals",
questions: ["France", "Japan", "Peru"],
listWithArrow() {
return this.questions.map(q => `${this.title}: ${q}`);
},
listWithFunction() {
return this.questions.map(function (q) {
return `${this?.title}: ${q}`;
});
},
};
console.log(quiz.listWithArrow());
console.log(quiz.listWithFunction());Line-by-line explanation
- Line 6The arrow function borrows
thisfromlistWithArrow, so it seesquiz. - Lines 10 to 12A normal function callback gets its own
this, which is notquiz, so the title is lost.
call, apply and borrowing methods
function introduce(greeting, punctuation) {
return `${greeting}, I'm ${this.name}${punctuation}`;
}
const ana = { name: "Ana" };
const ben = { name: "Ben" };
console.log(introduce.call(ana, "Hello", "!"));
console.log(introduce.apply(ben, ["Hi", "."]));
const sayHiAsAna = introduce.bind(ana, "Hi");
console.log(sayHiAsAna("?"));
const scores = [12, 48, 7];
console.log(Math.max.apply(null, scores), Math.max(...scores));Line-by-line explanation
- Line 8
callsetsthistoanaand lists the arguments. - Line 9
applyis the same but takes an array of arguments. - Line 11
bindfixesthisand the first argument, returning a new function. - Line 15
applyused to be the way to pass an array toMath.max; spread does it now.
Common JavaScript this Keyword Mistakes
Arrow function as a method
const cart = {
items: 3,
count: () => this.items, // this is not cart
};const cart = {
items: 3,
count() { return this.items; },
};Passing a method as a callback
button.addEventListener("click", menu.toggle); // this is the button, not menubutton.addEventListener("click", () => menu.toggle());
// or
button.addEventListener("click", menu.toggle.bind(menu));Calling bind and expecting it to run
report.bind(timer); // returns a new function, nothing happensreport.call(timer); // run now
const later = report.bind(timer); later(); // run laterJavaScript this Keyword in Real Projects
this in real code
Class-based code, including older React components, binds methods in the constructor or uses arrow functions in class fields to keep this. jQuery handlers rely on this being the element. Libraries use call and apply to run your callbacks with a particular context. Most modern code avoids the problem by using arrow functions for callbacks and event.currentTarget in handlers.
JavaScript this Keyword Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Create a version of show with this fixed to item.
const item = { name: "Lamp" };
function show() { return this.name; }
const showItem = show.(item);
console.log(showItem());
bind returns a new function with a fixed this.What is logged?
const a = { n: 1, get() { return this.n; } };
const b = { n: 2 };
b.get = a.get;
console.log(a.get(), b.get());In user.save(), what is this inside save?
What's the difference between call and bind?
stopwatch.start() should increase seconds by 1 every 50ms, and getLabel (a copy of the method) should still return the right label. Fix the this problems.
const stopwatch = {
seconds: 0,
label() { return `${this.seconds}s`; },
start() {
this.id = setInterval(function () {
this.seconds++;
}, 50);
},
};
stopwatch.start();
const getLabel = stopwatch.label;- Seconds increase
- The copied method keeps this
JavaScript this Keyword Quiz
5 questions to check what you learned.
When is the value of this decided for a normal function?
- When the function is written
- When the page loads
- It never changes
- When the function is called
Do arrow functions have their own this?
- No, they use this from the surrounding code
- Yes, always the window
- Yes, the arrow itself
- Only in classes
Which method takes arguments as an array?
- call
- apply
- bind
- map
In a normal function event listener, what is this?
- The event object
- The window, always
- The element the listener is attached to
- undefined
What is this inside a plain function call in strict mode?
- window
- The function itself
- null
- undefined
JavaScript this Keyword Challenge
A bank account that keeps this
Create an object account with:
owner: "Sam"andbalance: 0- A method
deposit(amount)that adds tothis.balanceand returns the new balance - A method
depositLater(amount, ms)that callsthis.deposit(amount)aftermsmilliseconds usingsetTimeoutwith an arrow function - A variable
quickDepositthat isaccount.depositbound toaccount
- deposit adds to the balance
- bound copy keeps this
- depositLater works after a delay
- Uses bind and an arrow function