JavaScript Interview Prep
this Keyword & Binding

Explicit Binding

Force `this` with call, apply, and bind

LinkedIn Hook

Imagine you have a friend's phone. Normally every call you make shows your friend's caller ID. But what if you could FORCE any caller ID you wanted on every call?

That's exactly what call, apply, and bind do in JavaScript.

They let you rip this out of the hands of implicit binding and pin it to whatever object you choose — including borrowing methods from one object and running them on another.

One returns a new function. Two fire the function immediately. One of them takes an array. Mixing them up is an instant red flag in an interview.

In this lesson I break down all three, with a real-world method-borrowing example, and the polyfill for bind that senior interviewers love to ask.

Read the full lesson -> [link]

#JavaScript #InterviewPrep #CallApplyBind #ExplicitBinding #Frontend #CodingInterview #Polyfill


Explicit Binding thumbnail


What You'll Learn

  • How call, apply, and bind differ in invocation and argument style
  • How to borrow methods from one object and run them on another
  • How to write a polyfill for Function.prototype.bind from scratch

Forcing this — The Explicit Rule

Imagine you have a friend's phone. Normally when you call someone from it, the caller ID shows your friend's number. But what if you could force any caller ID you want? That's explicit binding — you manually tell JavaScript which object this should be.

call() — Invoke immediately, pass arguments one by one

function introduce(greeting, punctuation) {
  console.log(greeting + ", I'm " + this.name + punctuation);
}

const person = { name: "Rakibul" };

introduce.call(person, "Hello", "!");
// "Hello, I'm Rakibul!"

apply() — Invoke immediately, pass arguments as an array

function introduce(greeting, punctuation) {
  console.log(greeting + ", I'm " + this.name + punctuation);
}

const person = { name: "Rakibul" };

introduce.apply(person, ["Hey", "!!"]);
// "Hey, I'm Rakibul!!"

bind() — Returns a NEW function with this permanently set

function introduce(greeting) {
  console.log(greeting + ", I'm " + this.name);
}

const person = { name: "Rakibul" };

const boundIntroduce = introduce.bind(person);
boundIntroduce("Hi");    // "Hi, I'm Rakibul"
boundIntroduce("Hello"); // "Hello, I'm Rakibul"

// Even calling with a different context won't change it
const other = { name: "Karim" };
boundIntroduce.call(other, "Hey"); // "Hey, I'm Rakibul" — bind wins!

call vs apply vs bind — Quick Comparison

MethodInvokes Immediately?Arguments FormatReturns
callYescall(thisArg, arg1, arg2, ...)Function result
applyYesapply(thisArg, [arg1, arg2, ...])Function result
bindNobind(thisArg, arg1, arg2, ...)New function

Memory trick: apply takes an array. bind gives you back a function.

Real-World Use Case — Borrowing Methods

const calculator = {
  value: 0,
  add: function(num) {
    this.value += num;
    return this;
  }
};

const myCounter = { value: 100 };

// Borrow the 'add' method from calculator but use it on myCounter
calculator.add.call(myCounter, 50);
console.log(myCounter.value); // 150

Polyfill for bind — A Classic Interview Question

Understanding how bind works under the hood is a common senior-level question.

Function.prototype.myBind = function(context, ...boundArgs) {
  const fn = this; // the original function

  return function(...callArgs) {
    return fn.apply(context, [...boundArgs, ...callArgs]);
  };
};

// Usage
function greet(greeting, punctuation) {
  return greeting + ", " + this.name + punctuation;
}

const person = { name: "Rakibul" };
const boundGreet = greet.myBind(person, "Hello");
console.log(boundGreet("!")); // "Hello, Rakibul!"

How the polyfill works:

  1. this inside myBind is the original function (because greet.myBind(...) uses implicit binding)
  2. We save it as fn
  3. We return a new function that, when called, uses apply to call the original function with the saved context
  4. We merge pre-set arguments (boundArgs) with new arguments (callArgs)

Explicit Binding visual 1


Common Mistakes

  • Forgetting that bind does NOT invoke the function — writing fn.bind(obj)() instead of fn.bind(obj), or expecting output when nothing ran.
  • Passing arguments to apply individually (apply(obj, a, b)) instead of as an array (apply(obj, [a, b])) — apply silently ignores extras.
  • Trying to "re-bind" a bound function — once bind pins this, later call/apply/bind calls on the bound function cannot change it.

Interview Questions

Q: What does call() do?

call() invokes the function immediately with a specified this value, passing any remaining arguments one by one after the thisArg.

Q: What does apply() do? How is it different from call()?

apply() also invokes the function immediately with a specified this, but it takes the arguments as a single array. call passes args individually; apply takes an array.

Q: What does bind() return?

bind() returns a brand new function where this is permanently locked to the provided object. It does not invoke the original function — you must call the returned function yourself.

Q: Can you re-bind a function already bound with bind()?

No. Once a function is bound with bind, calling bind again or even call/apply on the bound function will NOT change this. The first bind wins permanently.

Q: What's the difference between call, apply, and bind?

call and apply both invoke the function immediately with a specified this. The difference is that call takes arguments individually while apply takes them as an array. bind does NOT invoke the function — it returns a new function with this permanently bound.

Q: Write a polyfill for Function.prototype.bind.

See the myBind implementation above. The key insight is: save the original function reference, return a closure that calls fn.apply(context, mergedArgs) when invoked, merging pre-set boundArgs with fresh callArgs.


Quick Reference — Cheat Sheet

EXPLICIT BINDING — QUICK MAP

call(thisArg, a, b, c)    -> runs NOW, args individual
apply(thisArg, [a, b, c]) -> runs NOW, args as array
bind(thisArg, a, b)       -> returns NEW fn, never runs now

Memory trick:
  Apply  -> Array
  Bind   -> gives you Back a function

Method borrowing:
  other.fn.call(myObj, ...args)

bind is sticky:
  boundFn.call(anotherObj)  // ignored; bind wins

Polyfill skeleton:
  Function.prototype.myBind = function(ctx, ...pre) {
    const fn = this;
    return function(...later) {
      return fn.apply(ctx, [...pre, ...later]);
    };
  };

Previous: Implicit Binding -> The Dot Decides Next: new Binding -> The 4-Step Factory


This is Lesson 4.2 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page