Call, apply, and bind are methods for controlling the this value in function calls. They are essential for managing context in JavaScript.
Call method:
Invokes a function with specified this value and individual arguments. First parameter is this, rest are function arguments. Executes immediately. Use when you know arguments individually.
Apply method:
Invokes a function with specified this value and array of arguments. First parameter is this, second is array. Executes immediately. Use when you have arguments in an array.
Bind method:
Creates a new function with permanently bound this value. Returns new function, does not execute immediately. Can pre-set arguments for partial application. Use for event handlers, callbacks, or creating specialized functions.
Key differences:
Call and apply execute immediately, bind returns new function. Call takes comma-separated arguments, apply takes array. Bind permanently fixes this, call and apply use it for single invocation.
When to use call:
Function borrowing. Constructor chaining in inheritance. When you have individual arguments.
When to use apply:
When arguments are in an array. Using array methods on array-like objects. Math methods with array of numbers.
When to use bind:
Event handlers that need specific this. Callbacks that must maintain context. Partial application. Creating reusable specialized functions. React class component methods.
Common use cases:
Borrowing array methods for arguments or NodeList. Maintaining this in setTimeout or event handlers. Constructor chaining. Converting array-like to array. Function composition and currying.
All three methods are fundamental to JavaScript and appear frequently in interviews.
Example code
// CALL METHOD
const person = {
name: 'John',
greet: function(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
};
const anotherPerson = { name: 'Jane' };
// Use call to invoke with different this
person.greet.call(anotherPerson, 'Hello', '!'); // 'Hello, Jane!'
// Constructor chaining
function Animal(name, type) {
this.name = name;
this.type = type;
}
function Dog(name, breed) {
Animal.call(this, name, 'Dog'); // Call parent constructor
this.breed = breed;
}
const buddy = new Dog('Buddy', 'Labrador');
console.log(buddy); // {name: 'Buddy', type: 'Dog', breed: 'Labrador'}
// APPLY METHOD
// Same as call but with array
person.greet.apply(anotherPerson, ['Hi', '...']); // 'Hi, Jane...'
// Math.max with array
const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers);
console.log(max); // 7
// Modern alternative with spread
const max2 = Math.max(...numbers);
// Converting arguments to array
function toArray() {
return Array.prototype.slice.apply(arguments);
}
const arr = toArray(1, 2, 3, 4);
console.log(arr); // [1, 2, 3, 4]
// BIND METHOD
// Create new function with bound this
const greetJohn = person.greet.bind(person);
greetJohn('Hey', '!!!'); // 'Hey, John!!!'
// setTimeout problem and solution
const user = {
name: 'Alice',
printName: function() {
console.log(this.name);
}
};
// Problem: this is lost
setTimeout(user.printName, 1000); // undefined
// Solution: bind this
setTimeout(user.printName.bind(user), 1000); // 'Alice'
// Event handler
const button = {
label: 'Click me',
handleClick: function() {
console.log(this.label + ' was clicked');
}
};
// document.getElementById('btn')
// .addEventListener('click', button.handleClick.bind(button));
// Partial application with bind
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
const triple = multiply.bind(null, 3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// COMPARISON EXAMPLE
function introduce(greeting, profession) {
console.log(`${greeting}, I am ${this.name}, a ${profession}`);
}
const john = { name: 'John' };
// call - execute now, individual args
introduce.call(john, 'Hello', 'Developer');
// 'Hello, I am John, a Developer'
// apply - execute now, array of args
introduce.apply(john, ['Hi', 'Engineer']);
// 'Hi, I am John, a Engineer'
// bind - return new function
const johnIntro = introduce.bind(john);
johnIntro('Hey', 'Programmer');
// 'Hey, I am John, a Programmer'
// REAL-WORLD SCENARIOS
// Scenario 1: React class component
class MyComponent {
constructor() {
this.state = { count: 0 };
// Bind methods in constructor
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ count: this.state.count + 1 });
}
}
// Scenario 2: Array-like to Array
function convertToArray() {
return Array.prototype.slice.call(arguments);
}
// Scenario 3: Finding max in array of arrays
const data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const maxValues = data.map(arr => Math.max.apply(null, arr));
console.log(maxValues); // [3, 6, 9]
// Scenario 4: Safe hasOwnProperty
const obj = Object.create(null);
const hasProp = Object.prototype.hasOwnProperty.call(obj, 'key');
// IMPLEMENTATION EXAMPLES
// Simple call implementation
Function.prototype.myCall = function(context, ...args) {
context = context || globalThis;
const fnSymbol = Symbol();
context[fnSymbol] = this;
const result = context[fnSymbol](...args);
delete context[fnSymbol];
return result;
};
// Simple bind implementation
Function.prototype.myBind = function(context, ...boundArgs) {
const fn = this;
return function(...args) {
return fn.apply(context, [...boundArgs, ...args]);
};
};