call, apply and bind

Mental model: call and apply lend this for one call. bind welds it on for good.

Level: intermediate · about 13 minutes

Rule 2 from the last lesson gets its own lesson because it is the escape hatch. When the call site cannot give you the right this, these three methods let you supply it yourself.

function introduce(role, team) {
  return `${this.name} is ${role} on ${team}`;
}

const ada = { name: 'Ada' };

console.log(introduce.call(ada, 'lead', 'core'));      // args spread out
console.log(introduce.apply(ada, ['lead', 'core']));   // args in an array
console.log(introduce.bind(ada)('lead', 'core'));      // returns a function

Same target, same result, three different shapes.

MethodArgumentsCalls the function?Returns
fn.call(t, a, b)listed individuallyyes, immediatelywhatever fn returns
fn.apply(t, [a, b])one array (or array-like)yes, immediatelywhatever fn returns
fn.bind(t, a)optional preset argumentsnoa new, permanently bound function

Borrowing methods

A method is just a function that expects a particular shape of this. If your value has that shape, you can borrow the method even though it is not on your prototype.

const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };

// Not an array, so it has no join. Borrow one.
console.log(Array.prototype.join.call(arrayLike, '-'));   // 'a-b-c'
console.log(Array.prototype.map.call(arrayLike, (s) => s.toUpperCase())); // ['A','B','C']

// Borrow a safe hasOwnProperty for an object that may have shadowed it.
const risky = { hasOwnProperty: 'oops', real: 1 };
console.log(Object.prototype.hasOwnProperty.call(risky, 'real')); // true
const tag = (v) => Object.prototype.toString.call(v).slice(8, -1);

console.log(tag([]), tag(null), tag(new Date(0)), tag(/x/));
// 'Array' 'Null' 'Date' 'RegExp'
console.log(typeof [], typeof null); // 'object' 'object' - useless here

The classic type check: nothing else distinguishes these four.

Partial application with bind

Any argument you pass to bind after the this value is baked into the front of the argument list. That gives you a specialised function for free, no wrapper needed.

function log(level, module, message) {
  return `[${level}] ${module}: ${message}`;
}

const warn = log.bind(null, 'WARN');            // fix the first argument
const authWarn = warn.bind(null, 'auth');       // fix the second too

console.log(warn('db', 'slow query'));          // '[WARN] db: slow query'
console.log(authWarn('token expired'));         // '[WARN] auth: token expired'
function who() { return this.name; }

const first = who.bind({ name: 'first' });
const second = first.bind({ name: 'second' });

console.log(first());              // 'first'
console.log(second());             // 'first', not 'second'
console.log(first.call({ name: 'x' })); // 'first' - call cannot win either
console.log(first.name);           // 'bound who'
const obj = { v: 1, get() { return this.v; } };
const a = obj.get;
const b = obj.get.bind(obj);
const c = b.bind({ v: 99 });
console.log([b(), c(), typeof a]);

b is hard-bound to obj, so it returns 1. Rebinding a bound function does nothing to its this, so c() also returns 1. a is only referenced, never called, so no error is thrown.

Interactive visualiser: this. Enable JavaScript to use it.

Implementing bind yourself

This is a standard interview question because it forces you to combine closures, rest and spread, apply, and the new binding rule. Build it in two passes.

  1. Pass one: capture and forward Close over the target function, the this value and the preset arguments. Return a function that merges preset with later arguments.
  2. Pass two: respect new Rule 1 beats rule 2, so a bound function used with new must ignore the bound this and construct instead. new.target tells you which happened.
  3. Check it against the real thing The native version also drops .prototype, names itself bound fn, and reports a reduced length. Those details rarely matter; the two behaviours above always do.
`new.target`
the function new was called on, otherwise undefined
`fn.length` after bind
reduced by the number of preset arguments
`fn.name` after bind
prefixed with 'bound '
`Function.prototype.call.bind(fn)`
turns a method into a standalone function taking the receiver first
Cost of `bind`
a new function object each time, so do not bind inside a render loop

If you can write bind, you understand closures and this at the same time. That is why it is asked.

Every interviewer who has used this question

Try it yourself

Method borrowing

function collect() {
  // `arguments` is array-like, not an array
  return Array.prototype.slice.call(arguments).join('|');
}

console.log(collect('a', 'b', 'c'));

const nodeLike = { length: 2, 0: { id: 1 }, 1: { id: 2 } };
console.log(Array.prototype.map.call(nodeLike, (n) => n.id));
console.log(Array.from(nodeLike, (n) => n.id));

Rewrite each borrowed call using a modern equivalent (Array.from, spread, Object.hasOwn). Which reads better?

Partial application

const multiply = (a, b) => a * b;

const double = multiply.bind(null, 2);
const triple = multiply.bind(null, 3);

console.log(double(10), triple(10));   // 20 30
console.log(multiply.length, double.length); // 2 1

Add a fetchJson = request.bind(null, "GET") style helper. Then try binding the second argument only, and see why you cannot.

Exercises

Implement bind from scratch

Write myBind(fn, thisArg, ...preset). It returns a new function that calls fn with this set to thisArg, passing the preset arguments first and any later arguments after them. The bound function must not be hijackable with call, and when it is used with new it must construct an instance of fn instead of using thisArg. Do not call the native bind.

Implement call without call

Write myCall(fn, thisArg, ...args) that invokes fn with this set to thisArg, without using call, apply, bind or Reflect. The only remaining way to set this is implicit binding, so put the function on the object temporarily, call it through a dot, then clean up so the object is left exactly as you found it. Objectify primitive receivers, and use a fresh empty object when the receiver is null or undefined.

Check yourself

What does this log?
'one' 'one' — The first bind hard-binds this. The second bind wraps the already bound function, and that wrapper’s this is ignored by the inner bound function, so both calls report "one".
What is the difference between call and apply?
Only the argument shape: call takes a list, apply takes one array-like — They are identical apart from how arguments are passed. apply is still handy when the arguments already live in an array-like without a Symbol.iterator, where spread would fail.
Why does Object.prototype.toString.call(value) beat typeof value for identifying built-ins?
typeof returns "object" for arrays, dates, regexes and null, while toString exposes the internal tag — typeof collapses every non-function object to "object". Borrowing Object.prototype.toString reads the internal tag, giving "[object Array]", "[object Date]", "[object Null]" and so on. Array.isArray is still the right tool for arrays specifically.

Common mistakes

  • Expecting bind to call the function. It only returns a new one.
  • Rebinding an already bound function and expecting the new this to stick.
  • Calling bind inside a render or a loop and creating a fresh function object every pass.
  • Using apply with a huge array to spread arguments and blowing the argument limit. Chunk it instead.
  • Forgetting that bind also presets arguments, so fn.bind(null, x) shifts every later parameter along by one.

Takeaways

  • call and apply invoke immediately; only the argument shape differs.
  • bind returns a new function with this frozen, plus optional preset arguments.
  • A hard binding cannot be overridden by call or a second bind, but new still wins.
  • Borrowing prototype methods works because a method only cares about the shape of this.
  • Writing bind yourself is closures, spread and the new rule in ten lines.