How to control this
in JavaScript without losing your mind.
this
in JavaScript without losing your mind.Introduction
Few things confuse JavaScript developers more than this
. It behaves differently depending on how a function is called. To make matters worse, we also have three helpers—bind
, call
, and apply
—that let us manually control this
.
Good news: these aren’t magic. Once you see the patterns, they’re simple. Think of them as ways to borrow methods or lock in context.
In this guide, we’ll break them down with clear rules, copy-paste snippets, and real-world scenarios.
1. Why Do We Need These?
By default, this
is determined by how you call a function, not where it’s defined.
function greet() {
console.log("Hello, I’m " + this.name);
}
const user = { name: "Alice", greet };
user.greet(); // "Hello, I’m Alice"
const fn = user.greet;
fn(); // "Hello, I’m undefined" (or global object in non-strict mode)
Problem: once you detach greet
, this
gets lost.
Solution: use bind
, call
, or apply
to control this
.
2. call
: Invoke with a Specific this
Syntax:
func.call(thisArg, arg1, arg2, ...)
- Immediately invokes the function.
- Sets
this
tothisArg
. - Arguments are passed individually.
Example: Borrowing a method
function greet(greeting) {
console.log(greeting + ", I’m " + this.name);
}
const user = { name: "Alice" };
const admin = { name: "Bob" };
greet.call(user, "Hi"); // "Hi, I’m Alice"
greet.call(admin, "Hey"); // "Hey, I’m Bob"
👉 Think: “Call right now with this context.”
3. apply
: Like call
, but Args as an Array
Syntax:
func.apply(thisArg, [argsArray])
- Same as
call
, but arguments are given as an array (or array-like).
Example: Passing dynamic arguments
function sum(a, b, c) {
return a + b + c;
}
console.log(sum.apply(null, [1, 2, 3])); // 6
Real use: Math.max
with an array
const nums = [4, 7, 2];
console.log(Math.max.apply(null, nums)); // 7
👉 Modern alternative: spread syntax
console.log(Math.max(...nums)); // 7
4. bind
: Create a New Function with Fixed this
Syntax:
const boundFn = func.bind(thisArg, arg1, arg2, ...)
- Returns a new function with
this
permanently bound tothisArg
. - You can also preset arguments (partial application).
- Doesn’t run immediately — you call it later.
Example: Fixing lost context
const user = {
name: "Alice",
greet() {
console.log("Hi, I’m " + this.name);
}
};
setTimeout(user.greet, 1000);
// ❌ "Hi, I’m undefined"
setTimeout(user.greet.bind(user), 1000);
// ✅ "Hi, I’m Alice"
Example: Partial application
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
console.log(double(5)); // 10
👉 Think: “Bind later, call anytime.”
5. Visualizing the Difference

6. Real-World Examples
a) Borrowing Array Methods
const arrayLike = { 0: "a", 1: "b", length: 2 };
const realArray = Array.prototype.slice.call(arrayLike);
console.log(realArray); // ["a", "b"]
b) Event Handlers in React/DOM
class Button {
constructor(label) {
this.label = label;
this.handleClick = this.handleClick.bind(this); // fix `this`
}
handleClick() {
console.log(this.label);
}
}
Without bind
, this
would be undefined
in strict mode event handlers.
c) Variadic Utilities
function logArgs() {
console.log([].join.call(arguments, ", "));
}
logArgs("a", "b", "c"); // "a, b, c"
7. Common Gotchas
7.1 Losing this
const obj = { name: "Alice", greet() { console.log(this.name); } };
const fn = obj.greet;
fn(); // undefined
Fix: fn.bind(obj)
or always call obj.greet()
directly.
7.2 Overbinding
function say() { console.log(this.x); }
const obj = { x: 10 };
const bound = say.bind(obj);
bound.bind({ x: 20 })(); // still 10
👉 Once bound, this
can’t be rebound.
7.3 Arrow Functions Ignore bind
const obj = { name: "Alice" };
const arrow = () => console.log(this.name);
arrow.bind(obj)(); // still undefined
Arrow functions capture this
lexically—bind
has no effect.
8. Performance Notes
.bind
creates a new function every time—cache it if used in loops or renders..call
/.apply
overhead is negligible in most apps, but avoid in hot loops (prefer spread syntax).- Engines optimize common use cases, so choose readability over micro-optimizations.
Conclusion
bind
, call
, and apply
are simply ways to control this
and arguments:
- Use
call
when you want to run immediately with explicit arguments. - Use
apply
when arguments are in an array. - Use
bind
when you need a reusable function with fixedthis
(and maybe preset args).
Pro tip: When debugging this
, literally ask: “How is this function being called?” If you don’t like the answer, fix it with one of these three tools.
Call to Action
Which one tripped you up the most when you first learned JS — apply
’s array arguments, or arrow functions ignoring bind
?
💬 Drop your story in the comments.
🔖 Bookmark this as your this
survival guide.
👩💻 Share with a teammate who keeps saying “why is this undefined?”
Leave a Reply