The this Keyword (call, apply & bind)
Reviewed & published by Brayan K
this is the single most misunderstood word in JavaScript — and once it clicks, half of the "weird" bugs you've hit suddenly make sense. The trick: this is not decided by where a function is written, but by how it is called.
Part of the free JavaScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll learn the four call patterns that set this, why callbacks "lose" it, and how call, apply, and bind let you take back control.
What You'll Learn in This Lesson
- The golden rule: this depends on the call site
- Method, function, constructor & explicit binding
- Why callbacks lose their this
- Fixing this with arrow functions
- Borrowing methods with call and apply
- Locking this permanently with bind
📚 Prerequisites: You should be comfortable with functions and basic objects. If you know that objects hold key/value pairs and that methods are just functions stored on an object, you're ready.
💡 Running Code Locally: While this online editor runs real JavaScript, some advanced examples may have limitations. For the best experience:
- Download Node.js to run JavaScript on your computer
- Use your browser's Developer Console (Press F12) to test code snippets
- Create a .html file with <script> tags and open it in your browser
🗣️ Real-World Analogy: Think of the word "my" in a spoken sentence:
- • The sentence "I'll grab my coat" means something different depending on who is speaking
- • The words never change — only the speaker does
- • this works the same way: it points at whoever called the function
- • call/apply/bind let you choose the speaker on purpose
1️⃣ The Golden Rule: It's About the Call Site
Most beginners assume this refers to the object the function "belongs to". It doesn't. A function has no fixed this at all. The value is decided fresh every time the function is called, based on how the call is written. Look at the object immediately to the left of the dot at the moment of the call — that's usually your this.
function whoAmI() {
return this.name;
}
const alice = { name: "Alice", who: whoAmI };
const bob = { name: "Bob", who: whoAmI };
// The SAME function, but the object before the dot changes this
console.log(alice.who()); // ✅ Expected output: Alice
console.log(bob.who()); // ✅ Expected output: Bob
// Pulled off the object, there is no object before the dot:
const loose = alice.who;
// In strict mode (modules/classes) this would be undefined.
// We guard it so the demo never crashes:
console.log(typeof loose === "function"); // ✅ Expected output: true2️⃣ The Four Ways `this` Gets Set
Every function call falls into one of these four patterns. Learn them once and you'll never guess again:
- Method call — obj.fn() → this is obj
- Plain function call — fn() → this is undefined (strict) or the global object
- Constructor call — new Fn() → this is the brand-new object
- Explicit call — fn.call(obj) / fn.apply(obj) / fn.bind(obj) → this is whatever you pass
// Rule 1: method call — this is the receiver
const counter = {
count: 0,
inc() { this.count++; return this.count; },
};
console.log(counter.inc()); // ✅ Expected output: 1
console.log(counter.inc()); // ✅ Expected output: 2
// Rule 3: constructor call — this is the new instance
function Dog(name) {
this.name = name;
}
const rex = new Dog("Rex");
console.log(rex.name); // ✅ Expected output: Rex
// Rule 4: explicit binding — we choose this
function greet() { return "Hi " + this.name; }
console.log(greet.call({ name: "Sam" })); // ✅ Expected output: Hi Sam3️⃣ The Classic Bug: Losing `this` in a Callback
This is the bug that bites everyone. You have a method that works perfectly when you call it directly, but the moment you hand it to setTimeout, an event listener, or map, this vanishes. Why? Because those callers invoke your function as a plain function — there's no object before the dot anymore.
const timer = {
seconds: 0,
// ❌ Plain function passed to setTimeout loses 'this'
brokenStart() {
setTimeout(function () {
// 'this' here is NOT timer
return this.seconds; // would throw in strict mode
}, 0);
},
// ✅ Arrow function captures 'this' lexically
fixedTick() {
const run = () => { this.seconds += 1; };
run();
return this.seconds;
},
};
console.log(timer.fixedTick()); // ✅ Expected output: 1
console.log(timer.fixedTick()); // ✅ Expected output: 2
// Another fix: bind the callback to the object
function logName() { return this.label; }
const bound = logName.bind({ label: "dashboard" });
console.log(bound()); // ✅ Expected output: dashboard🎯 Your Turn #1 — Predict the Receiver
Fill in the blank so the method reads its own object's data.
const account = {
owner: "Priya",
balance: 100,
deposit(amount) {
// 👉 replace ___ so it updates THIS account's balance
this.___ += amount;
return this.balance;
},
};
console.log(account.deposit(50)); // ✅ Expected output: 150
console.log(account.deposit(25)); // ✅ Expected output: 1754️⃣ call() — Invoke Now With a Chosen `this`
fn.call(thisArg, arg1, arg2, ...) runs fn immediately, sets this to thisArg, and passes the rest as normal arguments. It's perfect for borrowing a method from one object to use on another.
function describe(role) {
return this.name + " works as a " + role;
}
const a = { name: "Lena" };
const b = { name: "Omar" };
// Borrow 'describe' for two different objects, passing args one by one
console.log(describe.call(a, "designer")); // ✅ Expected output: Lena works as a designer
console.log(describe.call(b, "developer")); // ✅ Expected output: Omar works as a developer
// Classic trick: turn an array-like into a real array
function sumArgs() {
const arr = Array.prototype.slice.call(arguments);
return arr.reduce((t, n) => t + n, 0);
}
console.log(sumArgs(1, 2, 3, 4)); // ✅ Expected output: 105️⃣ apply() — Same Idea, Arguments as an Array
fn.apply(thisArg, [args]) is identical to call except the arguments arrive as a single array. Remember it as "A for Array". It shines when you already have your values in an array and the function expects them as separate parameters.
const numbers = [5, 12, 8, 130, 44];
// Math.max expects max(a, b, c) — apply spreads the array for us
console.log(Math.max.apply(null, numbers)); // ✅ Expected output: 130
console.log(Math.min.apply(null, numbers)); // ✅ Expected output: 5
// call vs apply: only the argument shape differs
function intro(greeting, punctuation) {
return greeting + ", " + this.name + punctuation;
}
const person = { name: "Maya" };
console.log(intro.call(person, "Hello", "!")); // ✅ Expected output: Hello, Maya!
console.log(intro.apply(person, ["Hi", "..."])); // ✅ Expected output: Hi, Maya...6️⃣ bind() — Lock `this` Forever and Call Later
Unlike call and apply, bind does not run the function. It returns a new function whose this is permanently fixed. That makes it the go-to fix for event handlers and timers, where the framework decides when (and how) your function gets called. You can also pre-fill leading arguments — a technique called partial application.
const widget = {
label: "Save",
render() { return "[" + this.label + "]"; },
};
// Hand this off to a timer/event system safely
const safeRender = widget.render.bind(widget);
console.log(safeRender()); // ✅ Expected output: [Save]
// Once bound, this can NEVER be changed again — the first bind wins
const tricked = safeRender.bind({ label: "Cancel" });
console.log(tricked()); // ✅ Expected output: [Save]
// Partial application: pre-fill arguments
function multiply(a, b) { return a * b; }
const double = multiply.bind(null, 2);
console.log(double(10)); // ✅ Expected output: 20
console.log(double(7)); // ✅ Expected output: 14🎯 Your Turn #2 — Bind a Greeter
Create a bound copy of greet that is locked to the captain object.
function greet() {
return "Aye aye, " + this.title + "!";
}
const captain = { title: "Captain" };
// 👉 replace ___ so 'greet' is permanently bound to captain
const greetCaptain = greet.___(captain);
console.log(greetCaptain()); // ✅ Expected output: Aye aye, Captain!🧩 Reorder Challenge
These lines are scrambled. Put them in the right order so the bound function logs Hi Nova.
Why: you must declare the function and the object before you can bind them, and bind before you call.
🧠 Quick Recall
Predict the output before revealing the answer.
7 — called as o.get(), so this is o. (Calling f() instead would lose this.)
15 — this.a is locked to 10, and 5 is passed as b.
x9 — call invokes immediately with this set to the object we passed.
Common Errors & Fixes
- 1️⃣ Passing a method without binding
- 2️⃣ Using an arrow function as a method
- 3️⃣ Forgetting `new` on a constructor
- 4️⃣ Expecting call/apply to keep running later
Quick Reference — call / apply / bind
| Method | Runs now? | Arguments |
|---|---|---|
| call | ✅ Yes | One by one: fn.call(obj, a, b) |
| apply | ✅ Yes | As array: fn.apply(obj, [a, b]) |
| bind | ❌ No (returns fn) | Pre-fill: fn.bind(obj, a) |
| Arrow fn | Has no own this — inherits from the enclosing scope |
Lesson Complete — The this Keyword!
- ✅ this is set by the call site, not where the function is written
- ✅ Method, plain, constructor, and explicit calls each set this differently
- ✅ Callbacks lose this — fix with arrow functions or bind
- ✅ call/apply run now; bind returns a locked function for later
Practice quiz
According to the golden rule of this lesson, what decides the value of `this` inside a regular function?
- Where the function is written/defined
- How the function is called (the call site)
- The name of the function
- The file the function lives in
Answer: How the function is called (the call site). this is decided fresh every time, based on how the call is written — the call site, not where the function is defined.
Given `const o = { n: 7, get() { return this.n; } };`, what does `o.get()` log?
- undefined
- 0
- 7
- It throws an error
Answer: 7. Called as o.get(), the object before the dot is o, so this is o and this.n is 7.
In strict mode (modules and classes), what is `this` inside a plain function call like `fn()` with no object before the dot?
- The global/window object
- undefined
- An empty object {}
- The function itself
Answer: undefined. A plain function call has no receiver, so in strict mode this is undefined (it would be the global object in non-strict mode).
What is `this` inside a constructor call such as `new Dog('Rex')`?
- undefined
- The global object
- The brand-new object being created
- The Dog function
Answer: The brand-new object being created. A constructor call with new binds this to the freshly created instance.
Why do arrow functions help fix the 'lost this' problem inside a method's callback?
- They create their own new this each call
- They capture this lexically from the enclosing scope and have no own this
- They always set this to the global object
- They run faster than regular functions
Answer: They capture this lexically from the enclosing scope and have no own this. Arrow functions have no own this; they inherit this from the surrounding scope where they were created.
What does `describe.call({ name: 'Lena' }, 'designer')` return, given `function describe(role) { return this.name + ' works as a ' + role; }`?
- Lena works as a designer
- undefined works as a designer
- Lena works as a role
- It throws an error
Answer: Lena works as a designer. call invokes immediately with this set to {name:'Lena'} and passes 'designer' as the role argument.
Which statement about `call` versus `apply` is correct?
- call takes arguments as an array; apply one by one
- Both take arguments only as an array
- call takes arguments one by one; apply takes them as a single array
- apply does not run the function, it returns a new one
Answer: call takes arguments one by one; apply takes them as a single array. Both run the function immediately; call takes args one by one, apply takes them as one array ('A for Array').
What does `Math.max.apply(null, [5, 12, 8, 130, 44])` evaluate to?
- 5
- 130
- 199
- NaN
Answer: 130. apply spreads the array into separate arguments for Math.max, so it returns the largest value, 130.
How is `bind` different from `call` and `apply`?
- bind runs the function immediately
- bind returns a new function with this permanently fixed, to call later
- bind only works on arrow functions
- bind changes this every time it is called
Answer: bind returns a new function with this permanently fixed, to call later. bind does not run the function; it returns a new function whose this is locked in for later calls.
Given `const safe = widget.render.bind(widget);` then `const tricked = safe.bind({ label: 'Cancel' });`, what does `tricked()` log if widget.label is 'Save'?
- [Cancel]
- [Save]
- undefined
- It throws an error
Answer: [Save]. Once a function is bound, this can never be re-bound — the first bind wins, so it stays [Save].
Continue this course
- Previous: Scope & var/let/const (TDZ)
- Next: Destructuring Assignment — Pull values out of arrays and objects in one clean line
- Quick reference: JavaScript cheat sheet
Frequently asked questions
Why does `this` become undefined when I pass a method as a callback?
When you write button.addEventListener('click', user.greet), you pass the function value without its object. At call time there is no object before the dot, so in strict mode (which modules and classes use) `this` is undefined. Fix it with user.greet.bind(user), an arrow wrapper () => user.greet(), or by defining the method as an arrow-bound class field.
What is the difference between call, apply, and bind?
call and apply both invoke the function immediately with a chosen `this`; call takes arguments one by one, apply takes them as a single array. bind does not call the function — it returns a brand-new function with `this` (and optionally leading arguments) permanently locked in, which you call later.
Why don't arrow functions have their own `this`?
Arrow functions were designed to capture `this` from the surrounding scope at the moment they are created (lexical `this`). That is exactly what you want for callbacks inside a method, because the arrow keeps pointing at the same object instead of being re-bound by the caller.
Does bind work if I call the bound function with a different object?
No, and that is the point. Once a function is bound, its `this` can never be changed again — not by call, apply, or even another bind. The first bind wins. This makes bound methods safe to hand off to event systems and timers.
What is `this` at the top level of a script or module?
In a regular browser script `this` at the top level is the window object. Inside an ES module or in Node's module scope it is undefined or module.exports respectively, because module code runs in strict mode and is not attached to the global object.