Chapter 5.5☕ 18 min read

call, apply, bind & Partial Application

this ko force karo, arguments ko pehle se bharo — yeh hai explicit binding ka asli roop.

01call & apply — Abhi Chalao, Apna this Bhejo

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']
V8 optimization: V8 has optimized paths for .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.
02bind — Ek Baar Bhejo, Hamesha Ke Liye Lock Karo

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)
A bound function cannot be re-bound: Once you call .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.
03BoundFunctionExoticObject — Andar Se Kaise Kaam Karta Hai

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
📋 Remember:
• 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 arguments
04null & undefined — Strict Aur Sloppy Mein Farq

What 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!
})();
Why this matters: Some libraries intentionally pass 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.
05Modern Replacements — Spread Aur Arrow Ne Kiya Replace

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
bind for partial application survives: While arrows replace bind for 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:

  • call and apply invoke a function immediately with a specific this and arguments
  • bind returns a new function with this (and optionally arguments) permanently fixed
  • bind creates a BoundFunctionExoticObject — double binding ignores the second this but appends arguments
  • new overrides bind's this, but preserves bind's pre-filled arguments
  • ✅ In sloppy mode, passing null/undefined as thisArg coerces this to globalThis; strict mode keeps it null
  • ✅ Modern JS replaces apply with spread, and bind with arrows, but bind for partial application survives
Course Search
Search across all chapters & stages
📖

Search the course

Type any topic — branching, stash, rebase, hooks — and jump straight to that chapter.

merge branchesgit stashundo commitrebase