The Magic of this, call(), apply(), and bind() in JavaScript
What is this?
this = "Who called this function?"
Simple as that.
const person = {
name: "Alice",
greet: function() {
console.log("Hello, I'm " + this.name);
}
};
person.greet(); // "Hello, I'm Alice"
this refers to person because person called the greet method.
Think of this as a pronoun.
"I am Alice" → this.name is "Alice"
The value of this depends on how the function is called.
this in Different Contexts
1. In a regular function (standalone)
function showThis() {
console.log(this);
}
showThis(); // Window object (in browser) or undefined (strict mode)
No specific caller = this is the global object.
2. Inside an object method
const user = {
name: "Bob",
showName: function() {
console.log(this.name);
}
};
user.showName(); // "Bob"
user called the method → this = user
3. In arrow functions (special case)
const person = {
name: "Charlie",
greet: () => {
console.log(this.name); // undefined or window.name
}
};
person.greet();
Arrow functions don't have their own this. They inherit from surrounding scope.
Rule: Use regular functions for object methods if you need this.
Real-World Example
const car = {
brand: "Toyota",
model: "Camry",
start: function() {
console.log(this.brand + " " + this.model + " is starting");
}
};
car.start(); // "Toyota Camry is starting"
this.brand = car.brand = "Toyota"
this.model = car.model = "Camry"
this changes based on the caller.
const showInfo = car.start;
showInfo(); // "undefined undefined is starting"
Why? showInfo() is called standalone. No object context.
this becomes global object (no brand or model there).
This is where call(), apply(), and bind() help.
What is call()?
call() lets you call a function and manually set this.
Syntax:
functionName.call(thisValue, arg1, arg2, ...)
Example:
const person1 = {
name: "Alice"
};
const person2 = {
name: "Bob"
};
function greet() {
console.log("Hello, I'm " + this.name);
}
greet.call(person1); // "Hello, I'm Alice"
greet.call(person2); // "Hello, I'm Bob"
call() sets this to the object you pass.
With arguments:
function introduce(age, city) {
console.log("I'm " + this.name + ", " + age + " years old from " + city);
}
const person = { name: "Alice" };
introduce.call(person, 25, "Mumbai");
// "I'm Alice, 25 years old from Mumbai"
Pattern:
func.call(thisValue, arg1, arg2, arg3)
What is apply()?
apply() is like call(), but arguments are passed as an array.
Syntax:
functionName.apply(thisValue, [arg1, arg2, ...])
Example:
function introduce(age, city) {
console.log("I'm " + this.name + ", " + age + " years old from " + city);
}
const person = { name: "Bob" };
introduce.apply(person, [30, "Delhi"]);
// "I'm Bob, 30 years old from Delhi"
call() vs apply():
// call - arguments individually
introduce.call(person, 25, "Mumbai");
// apply - arguments as array
introduce.apply(person, [25, "Mumbai"]);
When to use apply():
When you already have arguments in an array.
const person = { name: "Charlie" };
const details = [28, "Bangalore"];
introduce.apply(person, details);
// "I'm Charlie, 28 years old from Bangalore"
What is bind()?
bind() creates a new function with this permanently set.
It doesn't call the function immediately.
Syntax:
const newFunc = functionName.bind(thisValue, arg1, arg2, ...)
Example:
const person = {
name: "Alice",
greet: function() {
console.log("Hello, I'm " + this.name);
}
};
const greetFunc = person.greet;
greetFunc(); // "Hello, I'm undefined" (lost context)
const boundGreet = person.greet.bind(person);
boundGreet(); // "Hello, I'm Alice" (context preserved)
Use case: Event handlers
const button = {
text: "Click me",
handleClick: function() {
console.log("Button says: " + this.text);
}
};
// Without bind
// document.getElementById('btn').addEventListener('click', button.handleClick);
// "Button says: undefined" (this = button element, not our object)
// With bind
// document.getElementById('btn').addEventListener('click', button.handleClick.bind(button));
// "Button says: Click me" (this = button object)
bind() with arguments:
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
console.log(double(5)); // 10 (2 * 5)
console.log(double(3)); // 6 (2 * 3)
First argument to bind() is this. Rest are pre-filled arguments.
call() vs apply() vs bind()
| Method | Arguments | Execution | Returns |
|---|---|---|---|
| call() | Individual: func.call(this, a, b) |
Immediate | Function result |
| apply() | Array: func.apply(this, [a, b]) |
Immediate | Function result |
| bind() | Individual: func.bind(this, a, b) |
Later (manual) | New function |
Visual comparison:
function introduce(age, city) {
console.log(this.name + ", " + age + ", " + city);
}
const person = { name: "Alice" };
// call - runs immediately, individual args
introduce.call(person, 25, "Mumbai");
// apply - runs immediately, array args
introduce.apply(person, [25, "Mumbai"]);
// bind - returns new function, call it later
const boundIntroduce = introduce.bind(person, 25, "Mumbai");
boundIntroduce(); // Call it when needed
Practical Examples
Example 1: Method borrowing with call()
const person1 = {
name: "Alice",
age: 25,
introduce: function() {
console.log("I'm " + this.name + ", " + this.age + " years old");
}
};
const person2 = {
name: "Bob",
age: 30
};
// Borrow person1's method for person2
person1.introduce.call(person2);
// "I'm Bob, 30 years old"
Example 2: Math operations with apply()
const numbers = [5, 10, 15, 20, 3];
// Find max using apply
const max = Math.max.apply(null, numbers);
console.log(max); // 20
// Find min
const min = Math.min.apply(null, numbers);
console.log(min); // 3
Math.max() expects individual arguments, not an array.
apply() converts array to individual arguments.
Example 3: Creating reusable functions with bind()
function calculatePrice(price, taxRate) {
return price + (price * taxRate);
}
// Pre-set tax rate for India
const calculateIndianPrice = calculatePrice.bind(null, 100);
console.log(calculateIndianPrice(0.18)); // 118
console.log(calculateIndianPrice(0.12)); // 112
// Better: curry-style
function multiply(a) {
return function(b) {
return a * b;
};
}
const double = multiply(2);
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
Practice Assignment
Task: Explore this, call(), apply(), and bind()
// 1. Create object with method using this
const student = {
name: "Alice",
grade: "A",
showDetails: function() {
console.log(this.name + " got grade " + this.grade);
}
};
student.showDetails(); // "Alice got grade A"
// 2. Borrow method using call()
const student2 = {
name: "Bob",
grade: "B"
};
student.showDetails.call(student2); // "Bob got grade B"
// 3. Function with arguments
function displayInfo(age, city) {
console.log(this.name + ", " + age + " years old, from " + city);
}
// Using call
displayInfo.call(student, 20, "Mumbai");
// "Alice, 20 years old, from Mumbai"
// Using apply
displayInfo.apply(student2, [22, "Delhi"]);
// "Bob, 22 years old, from Delhi"
// 4. Using bind
const boundDisplay = displayInfo.bind(student, 20, "Mumbai");
boundDisplay(); // "Alice, 20 years old, from Mumbai"
// Can call multiple times
boundDisplay();
boundDisplay();
// 5. Real-world: Array max/min
const scores = [85, 92, 78, 95, 88];
const maxScore = Math.max.apply(null, scores);
const minScore = Math.min.apply(null, scores);
console.log("Max:", maxScore); // 95
console.log("Min:", minScore); // 78
Common Mistakes
❌ Losing this context
const person = {
name: "Alice",
greet: function() {
console.log(this.name);
}
};
const greet = person.greet;
greet(); // undefined (lost context)
✅ Use bind() to preserve context
const greet = person.greet.bind(person);
greet(); // "Alice"
❌ Using arrow function for object methods
const person = {
name: "Bob",
greet: () => {
console.log(this.name); // undefined
}
};
✅ Use regular function
const person = {
name: "Bob",
greet: function() {
console.log(this.name); // "Bob"
}
};
❌ Confusing call and apply syntax
func.call(obj, [1, 2, 3]); // Wrong! Passes array as single arg
func.apply(obj, 1, 2, 3); // Wrong! apply needs array
✅ Remember the difference
func.call(obj, 1, 2, 3); // Individual args
func.apply(obj, [1, 2, 3]); // Array of args
Quick Reference
this:
const obj = {
method: function() {
console.log(this); // obj
}
};
call():
func.call(thisValue, arg1, arg2, arg3)
// Calls immediately, individual arguments
apply():
func.apply(thisValue, [arg1, arg2, arg3])
// Calls immediately, array arguments
bind():
const newFunc = func.bind(thisValue, arg1, arg2)
// Returns new function, call later
Memory trick:
Call = Comma separated (individual args)
Apply = Array (array args)
Bind = Bound (creates bound function)
You Now Understand this and Function Methods
What this means (the caller)this in different contextscall() - set this and call immediatelyapply() - like call() but with array argsbind() - create new function with fixed this
When to use each
When to Use Each
Use call() when:
Borrowing methods
Setting
thiswith few arguments
Use apply() when:
Arguments are already in an array
Using with Math.max/min
Use bind() when:
Event handlers
Creating reusable functions
Preserving context for later
Modern Alternative
Arrow functions often replace bind() for preserving context:
// Old way with bind
setTimeout(obj.method.bind(obj), 1000);
// Modern way with arrow
setTimeout(() => obj.method(), 1000);
But call() and apply() are still essential for method borrowing.
Functions are powerful in JavaScript.
Master this, call(), apply(), and bind() to unlock that power.
Leveling up your JavaScript? Follow for more advanced fundamentals.