---
title: 'call, apply, and bind'
source: 'https://academia.sh/en/courses/javascript-object-model/call-apply-bind'
course: 'Objects and Functions in JavaScript'
language: en
updated: '2026-08-17T18:09:50+00:00'
license: 'CC BY-SA 4.0'
---

# call, apply, and bind

Three operations that determine context at the call site, argument-passing forms, the function-borrowing pattern, partial application, and the identity cost of a permanent bond.

The previous lesson introduced explicit binding as a rule and showed that the
prototype chain determines where a function will be found while `this` determines
which data it operates on. Because the two are independent, an object can be run with
a method it has never heard of.

Three operations use this independence. `call` and `apply` fix context only for that
one call and run the function immediately; `bind` produces a **new function** that
carries the bond permanently. This lesson covers the differences among the three, the
borrowing pattern, and the cost of a permanent bond.

## Passing Arguments

The only difference between `call` and `apply` is how arguments are given: `call`
takes them one by one, `apply` as an array.

```js
const measurementBehavior = {
  summarize(prefix, separator) {
    return `${prefix}${separator}${this.sensor}${separator}${this.value}`;
  },
};

const record = { sensor: "S-01", value: 21.4 };
const args = ["measurement", " | "];

console.log(measurementBehavior.summarize.call(record, "measurement", " | "));
console.log(measurementBehavior.summarize.apply(record, args));
console.log(measurementBehavior.summarize.call(record, ...args));

const values = [21.4, 19.8, 25.1, 18.2];
console.log(Math.max.apply(null, values));
console.log(Math.max(...values));

try {
  measurementBehavior.summarize.call(null, "measurement", "-");
} catch (error) {
  console.log(`null context: ${error.constructor.name}`);
}
```

```
measurement | S-01 | 21.4
measurement | S-01 | 21.4
measurement | S-01 | 21.4
25.1
25.1
null context: TypeError
```

Once the spread operator entered the language, `apply`'s argument-unpacking role became
largely unnecessary; the third and fifth lines do the same job with spread. `apply`
still offers a direct path when the argument list is computed at runtime and held as an
array.

The last line fixes a detail: in strict mode, a `null` or `undefined` context is not
converted to the global object, it is passed as is. This is why
`Math.max.apply(null, ...)` works — `Math.max` never uses its `this` value — but a
function that reads `this` throws an error in the same kind of call.

## Function Borrowing

Explicit binding's real use is **function borrowing**: running a method not present in
an object's chain, on that object.

```js
const arrayLike = { 0: "S-01", 1: "S-02", 2: "S-03", length: 3 };

console.log(Array.prototype.join.call(arrayLike, ";"));
console.log(Array.prototype.map.call(arrayLike, (a) => a.toLowerCase()).join(","));
console.log(Array.from(arrayLike).join(","));
console.log(Array.isArray(arrayLike));

const dict = Object.create(null);
dict.sensor = "S-01";

console.log(typeof dict.hasOwnProperty);
console.log(Object.prototype.hasOwnProperty.call(dict, "sensor"));
console.log(Object.hasOwn(dict, "sensor"));

console.log(Object.prototype.toString.call([1, 2]));
console.log(Object.prototype.toString.call(null));
console.log(Object.prototype.toString.call(new Date(0)));
console.log(Object.prototype.toString.call({ sensor: "S-01" }));
```

```
S-01;S-02;S-03
s-01,s-02,s-03
S-01,S-02,S-03
false
undefined
true
true
[object Array]
[object Null]
[object Date]
[object Object]
```

The first three lines show **array-like** objects: objects carrying numeric keys and a
`length` property, but with no `Array.prototype` in their chain. Array methods only
look at these two things, so they can be borrowed. The fourth line confirms the object
is not really an array. `Array.from` does the same job more readably and is preferred
over borrowing in most cases.

The fifth and sixth lines connect to the first lesson's null-prototype dictionary. Such
an object has no `hasOwnProperty`, because it has no chain; the check can only be made
by borrowing from `Object.prototype`. `Object.hasOwn` is this borrowing built into the
language, giving the same result with less indirection.

The last four lines take advantage of the fact that `Object.prototype.toString`
produces a distinctive tag for every value type. This borrowing tells apart cases
`typeof` cannot — array versus object, `null` versus object.

## Permanent Bond and Partial Application

`bind` does not run the function; it returns a **new function** with the context, and
optionally the leading arguments, fixed. Fixing arguments in advance is called
**partial application**.

```js
function calibrate(offset, multiplier, rawValue) {
  return (rawValue + offset) * multiplier;
}

const sensorS01 = calibrate.bind(null, -0.4, 1.02);

console.log(calibrate(-0.4, 1.02, 21.4).toFixed(3));
console.log(sensorS01(21.4).toFixed(3));
console.log(sensorS01(19.8).toFixed(3));

console.log(calibrate.length);
console.log(sensorS01.length);
console.log(sensorS01.name);

const record = {
  sensor: "S-01",
  value: 21.4,
  format() {
    return `${this.sensor}: ${this.value}`;
  },
};

const boundFormat = record.format.bind(record);
const rebound = boundFormat.bind({ sensor: "S-99", value: 0 });

console.log(boundFormat());
console.log(rebound());
```

```
21.420
21.420
19.788
3
1
bound calibrate
S-01: 21.4
S-01: 21.4
```

Partial application is the direct way to produce a separate calibration function for
each sensor: the fixed parameters are given once, the varying one on every call. The
function's `length` property drops by the number of fixed arguments; its `name`
property states that it was derived.

The last two lines show a critical rule: **a bound function cannot be rebound.** The
second `bind` call produces a new function, but the inner bond does not change, so the
result is unchanged. This same rule was part of the previous lesson's priority order;
`new` is its one exception.

Formatting the floating-point outputs with `toFixed` is deliberate: printing the
expression `(21.4 - 0.4) * 1.02` directly would also show digits coming from the
binary-fraction representation. The How Computers Work course's Floating-Point Numbers
lesson explains this behavior.

## Preserving Context in Callbacks

The place the previous lesson's detachment problem shows up most is a method being
passed as a callback. There are three fixes, and all three give the same result.

```js
class MeasurementCollector {
  constructor(sensor) {
    this.sensor = sensor;
    this.values = [];
  }
  add(value) {
    this.values.push(value);
  }
  summary() {
    return `${this.sensor}: ${this.values.join(",")}`;
  }
}

const collector = new MeasurementCollector("S-01");
const incoming = [21.4, 19.8, 25.1];

try {
  incoming.forEach(collector.add);
} catch (error) {
  console.log(`no context: ${error.constructor.name}`);
}

incoming.forEach(collector.add.bind(collector));
console.log(collector.summary());

const second = new MeasurementCollector("S-02");
incoming.forEach(second.add, second);
console.log(second.summary());

const third = new MeasurementCollector("S-03");
incoming.forEach((value) => third.add(value));
console.log(third.summary());
```

```
no context: TypeError
S-01: 21.4,19.8,25.1
S-02: 21.4,19.8,25.1
S-03: 21.4,19.8,25.1
```

The first fix produces a permanent bond with `bind`. The second uses the context
argument some array methods, such as `forEach`, take as a second parameter — this
option is also found in `map`, `filter`, `some`, and `every`, but not in every
higher-order function. The third wraps the call in an arrow function and requires no
binding at all; the reason is the next lesson's subject.

## The Identity Cost of a Bond

Every time `bind` is called, it produces a new function object. This has consequences
everywhere identity comparison is relied on.

```js
const record = {
  sensor: "S-01",
  format() {
    return `record: ${this.sensor}`;
  },
};

const first = record.format.bind(record);
const second = record.format.bind(record);

console.log(first() === second());
console.log(first === second);
console.log(first === record.format);

class Listener {
  constructor(name) {
    this.name = name;
    this.handle = this.handle.bind(this);
  }
  handle(event) {
    return `${this.name} <- ${event}`;
  }
}

const a = new Listener("A");
const b = new Listener("B");

console.log(a.handle("measurement"));
console.log(Object.hasOwn(a, "handle"));
console.log(a.handle === b.handle);
console.log(Listener.prototype.handle === Object.getPrototypeOf(a).handle);
console.log(a.handle === Listener.prototype.handle);
```

```
true
false
false
A <- measurement
true
false
true
false
```

Two `bind` calls give two functions that produce the same result but are not equal to
each other. Because removing an event listener after registering it requires the same
function reference, the result of `bind` must be stored; a function rebound each time
cannot be removed.

The second part shows the effect binding done in a class constructor has on the
prototype model. The line `this.handle = this.handle.bind(this)` creates an **own
property** that shadows the prototype's method. The result: every instance carries its
own bound function, sharing across instances is lost, and the number of functions in
memory grows with the instance count. The original definition on the prototype stays
in place but is no longer used.

This is the reverse side of the first lesson's trade-off: shared behavior is cheap
when it stays on the chain, expensive but context-guaranteed once copied to the
instance. The Closures lesson will cover this trade-off again, in a more general form.

## Summary

- `call` and `apply` fix context only for that one call; the only difference is
  whether arguments are given one by one or as an array.
- In strict mode, a `null` or `undefined` context is not converted to the global
  object.
- Function borrowing runs a method on an object it is not in the chain of;
  array-like objects and null-prototype dictionaries are the typical cases.
- `bind` produces a new function, can partially fix arguments, and the bond it
  produces cannot be replaced by rebinding.
- Every `bind` call produces a new identity; for removable listeners, the result must
  be stored.
- Binding done in a constructor shadows the prototype's method with a per-instance
  copy and removes sharing.

## Next Step

In the previous section, the third fix for the callback problem was an arrow function,
and it required no binding operation at all. The reason is that arrow functions do not
take their `this` value from the call site — they have no context of their own. The
next lesson examines this behavior, the convenience it provides, and the cases where an
arrow function cannot be used as a method.
