Ways to bind 'this' in JavaScript
The Short Answer
JavaScript provides four ways to control what this refers to: bind() creates a new function with this permanently set, call() invokes a function immediately with a specified this, apply() does the same but takes arguments as an array, and arrow functions lexically inherit this from their enclosing scope. Understanding these is essential because this in JavaScript is determined by how a function is called, not where it's defined — and these methods let you override that behavior.
bind() — Create a Bound Function
bind() returns a new function with this permanently locked to the value you provide. It doesn't call the function — it creates a new version of it. This is useful when you need to pass a method as a callback but want to preserve its this context. Once bound, the this value cannot be changed, even with call or apply.
const user = {
name: 'Alice',
greet() {
return `Hello, I'm ${this.name}`;
},
};
// Problem: passing method as callback loses `this`
const greetFn = user.greet;
greetFn(); // "Hello, I'm undefined" — `this` is now global/undefined
// Solution: bind locks `this` permanently
const boundGreet = user.greet.bind(user);
boundGreet(); // "Hello, I'm Alice" ✅
// bind can also pre-fill arguments (partial application)
function multiply(a: number, b: number) {
return a * b;
}
const double = multiply.bind(null, 2); // `this` = null, first arg = 2
double(5); // 10
double(3); // 6
call() and apply() — Invoke Immediately
Unlike bind(), both call() and apply() invoke the function immediately with the specified this value. The only difference between them is how they accept arguments: call() takes them individually (comma-separated), while apply() takes them as an array. A helpful mnemonic: Call = Commas, Apply = Array.
function introduce(greeting: string, punctuation: string) {
return `${greeting}, I'm ${this.name}${punctuation}`;
}
const person = { name: 'Alice' };
// call — arguments passed individually
introduce.call(person, 'Hi', '!'); // "Hi, I'm Alice!"
// apply — arguments passed as array
introduce.apply(person, ['Hey', '.']); // "Hey, I'm Alice."
// Real-world use: borrowing methods
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const realArray = Array.prototype.slice.call(arrayLike); // ['a', 'b', 'c']
// Finding max in array (before spread existed)
const numbers = [5, 2, 8, 1];
Math.max.apply(null, numbers); // 8
// Modern equivalent: Math.max(...numbers)
In modern JavaScript, apply is less common because the spread operator handles the array-to-arguments conversion more cleanly. However, call is still useful for method borrowing — calling a method from one object's prototype on a different object.
Arrow Functions — Lexical this
Arrow functions don't have their own this — they capture this from the surrounding scope at the time they're defined. This makes them immune to the 'lost this' problem that plagues regular functions when used as callbacks. You can't override an arrow function's this with bind, call, or apply — it's permanently locked to the lexical scope.
class Timer {
seconds = 0;
// ❌ Regular function — `this` is lost in setTimeout callback
startBroken() {
setInterval(function () {
this.seconds++; // `this` is the global object, not Timer
}, 1000);
}
// ✅ Arrow function — `this` is inherited from startFixed()
startFixed() {
setInterval(() => {
this.seconds++; // `this` is the Timer instance ✅
}, 1000);
}
}
// Arrow functions can't be rebound
const obj = { name: 'Alice' };
const arrow = () => this; // `this` is whatever it was when defined
arrow.call(obj); // Still the outer `this`, NOT obj
arrow.bind(obj)(); // Still the outer `this`, NOT obj
Comparison
| Method | Invokes immediately? | Arguments format | Can rebind later? | Use case |
|---|---|---|---|---|
| bind() | No — returns new function | Individual (partial application) | No — permanently bound | Event handlers, callbacks |
| call() | Yes | Individual (comma-separated) | N/A — already called | Method borrowing, immediate invocation |
| apply() | Yes | Array | N/A — already called | Passing arrays as arguments (legacy) |
| Arrow function | N/A — defines function | N/A | No — lexically locked | Callbacks, class methods, closures |
Modern Best Practices
In modern codebases, arrow functions handle most this-binding needs automatically. You rarely need explicit bind, call, or apply anymore. Here's the decision tree for choosing the right approach in today's JavaScript.
Prefer these approaches
- ✅Arrow functions for callbacks and class methods — automatic lexical this
- ✅bind() when you must pass a regular function as a callback and preserve this
- ✅call() for method borrowing (Array.prototype.slice.call on array-likes)
Avoid these
- ❌apply() for spreading arrays — use spread operator instead
- ❌bind() in render methods — creates new function every render (use arrow class fields)
- ❌Relying on implicit this binding — always be explicit about what this refers to
Why Interviewers Ask This
This question tests your understanding of one of JavaScript's most confusing features. Interviewers want to see that you know the difference between bind (returns function) vs call/apply (invokes immediately), understand why arrow functions solve most this-binding problems, can identify when this gets lost (callbacks, event handlers, setTimeout), and know the modern best practices. It's a fundamental JavaScript question that separates developers who understand the language from those who just use frameworks.
Quick Revision Cheat Sheet
bind(): Returns new function with this locked — doesn't invoke
call(): Invokes immediately — args as commas
apply(): Invokes immediately — args as array
Arrow functions: Inherit this from enclosing scope — can't be rebound
Mnemonic: Call = Commas, Apply = Array, Bind = returns Bound function
Modern default: Use arrow functions — they handle this automatically