Topic 7.2
call, apply and bind
In one line
Every function has three methods that let you choose this yourself: call runs the function now with arguments listed one by one, apply runs it now with arguments in an array, and bind doesn't run it at all but returns a new function whose this (and optionally first arguments) are fixed for good.
Think of it like this
A rubber stamp that says "Paid by ___". **call is stamping a bill right now and writing the name in the blank: "Paid by Asha". apply is the same, but the details arrive on a list you hand over. bind is ordering a new stamp** with "Asha" already carved into the blank: you don't stamp anything yet, but every later use of that stamp says "Asha", and nobody can rub it out.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Explicit binding
- Choosing
thisyourself withcall,applyorbind, instead of letting the call site decide. - thisArg
- The first argument of
call,applyandbind: the valuethisshould be. - call
- Runs a function right now with a chosen
thisand arguments listed one by one. - apply
- Runs a function right now with a chosen
thisand the arguments in one array. - bind
- Makes a new function with
this(and maybe some arguments) fixed forever. It doesn't run anything. - Bound function
- The new function
bindreturns. Itsthiscan't be changed by how it's called (onlynewoverrides it). - Partial application
- Fixing some of a function's arguments now, and giving the rest later.
- Method borrowing
- Running a method that belongs to one kind of object on a different object, using
callorapply. - Array-like
- An object with a
lengthand numbered keys (0,1, ...) that isn't a real array, like a string orarguments.
Step by step
01A function that needs this
introduce reads this.name, but no object owns it. Called plainly in strict mode it would fail. We want to run it for Asha without adding it to her object.
"use strict";
function introduce(greeting, mark) {
return greeting + ", I am " + this.name + mark;
}
const asha = { name: "Asha" };02call: run it now, arguments with commas
call puts asha in the this slot and passes the rest as normal arguments.
introduce.call(asha, "Hello", "!"); // "Hello, I am Asha!"03apply: run it now, arguments in an array
Same result, but the arguments travel together in an array. Useful when they already are an array.
const args = ["Hi", "."];
introduce.apply(asha, args); // "Hi, I am Asha."
introduce.call(asha, ...args); // the modern way to do the same04bind: make a new function for later
bind returns a function and runs nothing. Here this and the first argument are fixed; the second argument comes later.
The new function is a real function you can store, pass to setTimeout, or use as an event listener.
const ashaSaysHey = introduce.bind(asha, "Hey");
ashaSaysHey("!!"); // "Hey, I am Asha!!"
ashaSaysHey("?"); // "Hey, I am Asha?"05A bound function can't be re-bound
Binding again, using call, or attaching the bound function to another object all leave this as asha. The first bind wins forever.
This is why bound functions are safe to pass around, and also why you can't "fix" a function someone else already bound.
const ravi = { name: "Ravi" };
ashaSaysHey.call(ravi, "!"); // still "Hey, I am Asha!"
ashaSaysHey.bind(ravi)("!"); // still AshaTry it yourself
- 1
Bind with new arguments
In the first example, add
const raviAsks = introduce.bind(ravi, "Excuse me");and lograviAsks("?"). Predict the text before running. - 2
Remove the wrong function
In the second example, change
button.removeEventListener("click", handler);tobutton.removeEventListener("click", counter.increment.bind(counter));. Predict the last count. Why didn't the listener go away? - 3
Try call on an arrow
Add
const arrow = () => this; console.log(arrow.call({ x: 1 }) === window);to the third example. Predicttrueorfalse, then check.
Code & diagrams
A bound function's `name` starts with `bound `. The last two lines tried to switch to Ravi and failed: the first `bind` is permanent.
Preview
Console
Nothing logged yet.Expected console output
Hello, I am Asha!
Hi, I am Ravi.
function bound introduce
Hey, I am Asha!!
Hey, I am Asha?
Hey, I am Asha?Keep the bound function in a variable: `removeEventListener` needs the **same** function. Calling `bind` again would make a different one, and nothing would be removed.
Preview
Console
Nothing logged yet.Expected console output
count: 2
after removing, count: 2`double.length` is 1 because one of the two parameters is already filled. `apply` and spread give the same answer; spread is the modern way.
Preview
Console
Nothing logged yet.Expected console output
10 15 length: 1
[ 'A', 'B', 'C' ]
tea + coffee
[object Array] [object Null] [object Date]
9 9Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Pass the result of call instead of a function
Write button.addEventListener("click", counter.increment.call(counter));.
Break #2
Bind inside removeEventListener
Add with el.addEventListener("click", obj.go.bind(obj)) and remove with el.removeEventListener("click", obj.go.bind(obj)).
Myth vs fact
Myth
bind changes the original function.
Fact
It returns a brand-new function and leaves the original untouched. obj.method still loses this when detached; only the bound copy is safe.
Myth
apply is faster than call, or the other way round.
Fact
In modern engines the difference is negligible. Pick by how your arguments arrive; in new code spread syntax usually replaces apply.
Myth
You can bind an arrow function to an object.
Fact
arrow.bind(obj) returns a function, but its this is still the one from where the arrow was written. Only the extra arguments are fixed.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
A bound function is a special bound function exotic object with internal slots
[[BoundTargetFunction]],[[BoundThis]]and[[BoundArguments]]. It has noprototypeproperty of its own;new bound()constructs the target function, ignores[[BoundThis]], and still prepends the bound arguments. - ▸
fn.lengthof a bound function ismax(0, target.length - boundArgs.length)and itsnameis"bound " + target.name. Stack traces and DevTools show that name, which helps when debugging. - ▸
Engines optimise
applywith an array literal andcallwell, butapplywith a very large array (hundreds of thousands of items) can throwRangeError: Maximum call stack size exceeded, because every item becomes a separate argument on the stack. The same limit hits spread:Math.max(...hugeArray). Use a loop orreducefor huge arrays. - ▸
If someone may have overwritten a method on an object (or the object has a null prototype), call the original from the prototype:
Object.prototype.hasOwnProperty.call(obj, key). ES2022'sObject.hasOwn(obj, key)exists to replace exactly this pattern.
Remember this
- 1
Functions in JavaScript are objects, and every function inherits three methods from
Function.prototype(you'll see what that means in Topic 7.3):call,applyandbind. Each takes as its first argument the value you wantthisto be. This is called explicit binding, because you say whatthisis instead of letting the call site decide. - 2
**
fn.call(thisArg, a, b)** callsfnimmediately, withthisset tothisArgand the remaining arguments passed one by one.greet.call(asha, "Hello", "!")works exactly likeasha.greet("Hello", "!")would, even thoughashahas nogreetproperty. It returns whatever the function returns. - 3
**
fn.apply(thisArg, [a, b])is the same, but the arguments come as one array (or array-like object). Mnemonic: apply takes an array, call takes c**ommas. Before spread syntax (ES2015) this was the only way to pass an array as separate arguments:Math.max.apply(null, nums). TodayMath.max(...nums)does the same and reads better, soapplyis mostly seen in older code. - 4
**
fn.bind(thisArg, a)does not** callfn. It returns a new bound function: calling it later callsfnwiththisfixed tothisArgand withaplaced before any new arguments. The binding is permanent: calling the bound function as a method, throughcall, or binding it again can't changethis. Onlynewoverrides it (thenthisis the new object). That makesbindthe classic fix for lostthis:button.addEventListener("click", counter.increment.bind(counter)). - 5
bindalso does partial application: fixing some arguments now and supplying the rest later.const double = multiply.bind(null, 2)makes a function that always passes2as the first argument, sodouble(5)is10. PassnullasthisArgwhen the function doesn't usethis. - 6
Borrowing methods is the other big use. Many built-in methods work on anything that looks right, so you can run them on an object that doesn't own them:
Array.prototype.map.call("abc", (c) => c.toUpperCase())maps over a string's characters, andObject.prototype.toString.call(value)gives a reliable type tag like[object Array]. Arrow functions ignore all three: theirthiscomes from where they were written, soarrow.call(obj)passes the arguments but leavesthisalone.
Explain it without notes
What is the difference between call, apply and bind?
Why can't a bound function's this be changed, and is there any exception?
Write a simple version of Function.prototype.bind and explain it.
What is method borrowing? Give two real examples.
Practice
Use call to run function describe(unit) { return this.amount + " " + unit; } for { amount: 3 } with the unit "kg", and apply to do the same with { amount: 5 } and ["litres"].
Make const addTax = applyRate.bind(null, 0.18); from function applyRate(rate, price) { return price + price * rate; } and print addTax(100) and addTax(250).
Write logAll() that prints Math.min of its own arguments by borrowing: use Math.min.apply with the arguments object, and then the modern rest-parameter version.
Trade-offs
- ↔
bindgives a stable, reusable function (good for add/remove listener pairs), but each call allocates a new function; an inline arrow() => obj.m()is just as clear for one-off callbacks. - ↔
Borrowing prototype methods works on any array-like, but
Array.from(x)or spread make an honest array first and are easier to read; borrow only when copying would be wasteful.
Done when you can
I can use
callandapplyto run a function with a chosenthis.I can use
bindto make a function withthisfixed, and keep it forremoveEventListener.I can use
bindfor partial application.I know a bound function can't be re-bound and arrows ignore all three.
I can borrow array and object methods with
call.