call, apply, bind & Partial Application
this ko force karo, arguments ko pehle se bharo — yeh hai explicit binding ka asli roop.
call and apply let you invoke a function with an explicit this value — immediately. Think of it like borrowing someone else's method for your own object.
fn.call(thisArg, arg1, arg2): Invoke immediately, this = thisArg, individual arguments.
fn.apply(thisArg, [argsArray]): Invoke immediately, this = thisArg, arguments as array.
The only difference: how you pass arguments. call takes them one by one, apply takes an array.
function greet(greeting) {
console.log(greeting + ', ' + this.name);
}
const user = { name: 'Sai' };
// .call() — immediate, individual args
greet.call(user, 'Hello'); // 'Hello, Sai'
// .apply() — immediate, array of args
greet.apply(user, ['Namaste']); // 'Namaste, Sai'
// Classic apply use case: Math.max on array
const numbers = [5, 2, 9, 1];
const max = Math.max.apply(null, numbers); // 9 (before spread)
// Modern equivalent: Math.max(...numbers)
// Borrowing array methods for arguments
function logArgs() {
// arguments is array-like, not array
const args = Array.prototype.slice.call(arguments);
console.log(args);
}
logArgs('a', 'b', 'c'); // ['a', 'b', 'c']
.call() when the receiver type is consistent. If TurboFan sees fn.call(obj) where obj's hidden class doesn't change, it can inline the call entirely, avoiding the overhead of the Function.prototype.call logic.bind doesn't invoke the function immediately. Instead, it returns a new function with this permanently locked to the value you provide.
fn.bind(thisArg): Returns a new function where this = thisArg, always.
The bound function can be called later — this is already baked in. This is why bind is essential for callbacks and event handlers.
Partial Application: bind doesn't just lock this — it can also pre-fill arguments.
const module = {
x: 42,
getX: function() { return this.x; }
};
const unboundGetX = module.getX;
console.log(unboundGetX()); // undefined (this = global)
// Bind it permanently to module
const boundGetX = module.getX.bind(module);
console.log(boundGetX()); // 42 ✅
// Partial Application — pre-fill arguments
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2); // pre-fill 'a' as 2
console.log(double(5)); // 10 (2 * 5)
console.log(double(10)); // 20 (2 * 10)
const triple = multiply.bind(null, 3);
console.log(triple(4)); // 12 (3 * 4)
.bind(), the this is permanently locked. Even calling .bind() again on the result won't change this. This is because bind creates a special internal object — we'll explore this in the next section.bind doesn't just return a regular function. Under the hood, it creates something the spec calls a BoundFunctionExoticObject.
This special object stores three internal slots:
[[BoundTargetFunction]] → the original function
[[BoundThis]] → the locked this value
[[BoundArguments]] → array of pre-filled arguments
When you call the bound function, the engine internally does:
// Conceptually:
boundFn(...newArgs)
→ [[BoundTargetFunction]].apply(
[[BoundThis]],
[...[[BoundArguments]], ...newArgs]
)
Double bind: If you call .bind() on an already-bound function, the new this is ignored, but new arguments are appended.
new with bound: The new keyword overrides the bound this, but preserves the bound arguments.
function original(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const obj1 = { name: 'Sai' };
const obj2 = { name: 'Ali' };
// First bind
const bound1 = original.bind(obj1, 'Hello');
bound1('!'); // 'Hello, Sai!'
// Second bind — this (obj2) is IGNORED
const bound2 = bound1.bind(obj2, 'Hey');
bound2(); // 'Hello, Sai!' — obj2 didn't work
// bound1 already has 'Hello' as first arg
// new overrides bound this
function Item(name) {
this.name = name;
}
const boundItem = Item.bind({ name: 'Wrong' }, 'Correct');
const instance = new boundItem();
console.log(instance.name); // 'Correct' — new won
• When a function is bound multiple times, only the FIRST bind's
this matters• Subsequent binds can only add more pre-filled arguments
•
new overrides the bound this but keeps the bound argumentsWhat happens when you pass null or undefined as the thisArg? It depends on strict mode.
Sloppy mode: null or undefined is coerced to globalThis (window in browsers).
Strict mode: null stays null, undefined stays undefined.
// Sloppy mode — null becomes globalThis
function showThis() {
console.log(this);
}
showThis.call(null); // window (global object)
// Strict mode — null stays null
(function() {
'use strict';
function strictShowThis() {
console.log(this);
}
strictShowThis.call(null); // null
strictShowThis.call(undefined); // undefined
})();
// Primitive boxing (sloppy)
function boxTest() {
console.log(typeof this, this);
}
boxTest.call('hello'); // object, String('hello') — boxed!
boxTest.call(42); // object, Number(42) — boxed!
// Primitive stays primitive (strict)
(function() {
'use strict';
function strictBoxTest() {
console.log(typeof this, this);
}
strictBoxTest.call('hello'); // string, 'hello' — no boxing!
})();
null as thisArg when they don't care about this. In strict mode, the function gets null as this. In sloppy mode, it gets the global object. This difference can cause subtle bugs when mixing strict and non-strict code.Modern JavaScript has replaced many call/apply/bind patterns with cleaner alternatives.
apply → spread: fn(...args) replaces fn.apply(null, args).
bind for this → arrow functions: () => obj.method() replaces obj.method.bind(obj).
slice.call → Array.from: [...arguments] replaces Array.prototype.slice.call(arguments).
const numbers = [5, 2, 9, 1];
// OLD: apply for array arguments
Math.max.apply(null, numbers); // 9
// NEW: spread
Math.max(...numbers); // 9 ✅
// OLD: bind for this in callbacks
class Timer {
constructor() { this.seconds = 0; }
start() {
setTimeout(this.tick.bind(this), 1000);
}
tick() { this.seconds++; }
}
// NEW: arrow function
class ModernTimer {
constructor() { this.seconds = 0; }
start() {
setTimeout(() => this.tick(), 1000); // ✅
}
tick() { this.seconds++; }
}
// Partial application — bind still useful, but arrow is cleaner
function add(a, b) { return a + b; }
const add5bind = add.bind(null, 5);
const add5arrow = (b) => add(5, b); // often clearer
this, bind is still the cleanest way to pre-fill arguments. multiply.bind(null, 2) is more declarative than (b) => multiply(2, b). Use bind when you want to say "pre-fill these args", use arrows when you need to lock this.Lo kar liya — Key Points:
- ✅
callandapplyinvoke a function immediately with a specificthisand arguments - ✅
bindreturns a new function withthis(and optionally arguments) permanently fixed - ✅
bindcreates a BoundFunctionExoticObject — double binding ignores the secondthisbut appends arguments - ✅
newoverrides bind'sthis, but preserves bind's pre-filled arguments - ✅ In sloppy mode, passing
null/undefinedas thisArg coercesthisto globalThis; strict mode keeps it null - ✅ Modern JS replaces
applywith spread, andbindwith arrows, butbindfor partial application survives
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login