call, apply & bind
Learn how call, apply, and bind set this explicitly, pass arguments, borrow methods, create partial applications, and build a simplified bind helper.
- 01Choose call, apply, or bindPick
callto run a function now with arguments listed one by one,applyto run it now with an array, orbindto make a new function for later. - 02Borrow a methodRun an array or object method on another value that has what the method needs, such as
argumentsor a string. - 03Write a small bind helperWrite
myBind, which works like the built-inbindfor normal calls, usingapply, rest parameters, and spread.
Choosing this yourself
In the last lesson, The this keyword, you learned that this is the object a function works on. You also learned four rules that decide what this is. One of those rules lets you choose this yourself. This lesson is about that rule.
Functions in JavaScript are objects, so they have methods too. A method is a function stored on an object. Every function has three methods for choosing this:
callruns the function now, with thethisyou choose.applyalso runs the function now. It takes the other arguments inside one array.binddoes not run the function. It makes a new function that remembers yourthisfor later.
None of the three changes the original function. They only decide how one call runs.
You borrow your friend Ada's phone to make one call. When the call ends, you give the phone back. Using the phone with a list of what to say is the same, just read from a list. Saving Ada on speed dial is different: nothing happens now, but Ada is ready the moment you tap later.
- In real life: Use Ada's phone for one call
- In JavaScript:
introduce.call(ada, "Hello", "!") - In real life: Use Ada's phone and read your words from a list
- In JavaScript:
introduce.apply(ada, ["Hello", "!"]) - In real life: Save Ada on speed dial
- In JavaScript:
introduce.bind(ada)returns a new function
call, apply, and bind are methods that every function has. Functions get them from Function.prototype, the shared object that holds the methods of all functions. They let you run a regular function with a this value that you pick. bind can also save some arguments for later.
There is one exception. An arrow function, written () => ..., has no this of its own. It uses the this of the code around it.
function makeGreeter() {
const greet = () => this.name;
return greet;
}
const ada = { name: "Ada" };
const greetAda = makeGreeter.call(ada);
console.log(greetAda.call({ name: "Grace" }));Line 2 makes an arrow function inside makeGreeter. The arrow uses the same this as makeGreeter. Line 7 runs makeGreeter with call, so its this is ada. Line 8 tries to run the arrow with Grace as this. It still prints Ada. So call, apply, and bind can't change this for an arrow function.
You will also borrow methods from built-in objects, and write a small myBind function of your own. Two later lessons go deeper: Currying & partial application and Polyfill call, apply & bind. This lesson covers what each method does and when to use it.
call and apply run the function now
INTERACTIVEcall and apply are almost the same. Both run the function right away. For both, the first argument is the value to use as this. The only difference is how you pass the other arguments:
calltakes them one by one, with commas:introduce.call(ada, "Hello", "!").applytakes them inside one array:introduce.apply(ada, ["Hello", "!"]).
An easy way to remember: apply and array both start with "a".
The table compares all three methods. Read across a row to see what one method does.
| Method | When does it run, and what do you get back? | How do you pass the arguments? |
|---|---|---|
| call | It runs now. You get back the function's result. | You list them one by one, with commas. |
| apply | It runs now. You get back the function's result. | You put them all in one array. |
| bind | It doesn't run now. You get back a new function to call later. | You list the ones to save, one by one. |
Try it below. The function introduce reads this.name. Pick a method and an object. The last line of the code is what you would type in the console. The result comes from running real JavaScript.
function introduce(greeting, punctuation) { return `${greeting}, I'm ${this.name}${punctuation}`;} const ada = { name: "Ada" };const grace = { name: "Grace" };const linus = { name: "Linus" }; introduce.call(ada, "Hello", "!");this isAdaintroduce.call(ada, "Hello", "!")call runs introduce now with the object you picked as this. The other arguments are listed one by one.
The result depends on how you call introduce, not on where it was written. introduce.call(ada, "Hello", "!") means: "Run introduce now, and inside it, make this be ada." The apply version means the same thing. It just takes ["Hello", "!"] as one array.
Step through the code below. Line 8 uses call and line 9 uses apply.
Guess each result before you step. call and apply both run the function right away. Only the way you pass the arguments is different.
script
return `${greeting}, I'm ${this.name}${punctuation}`;} const ada = { name: "Ada" };const grace = { name: "Grace" }; introduce.call(ada, "Hello", "!");introduce.apply(grace, ["Hi", "."]);introduce right away. The first value you pass goes into this. call takes the other arguments one by one. apply takes them inside one array.The spread syntax, ...numbers, also takes the items out of an array and passes them one by one. Math.max returns the largest number you pass to it.
const numbers = [4, 9, 2];
console.log(Math.max.apply(null, numbers));
console.log(Math.max(...numbers));
const argumentBag = { 0: 4, 1: 9, 2: 2, length: 3 };
console.log(Math.max.apply(null, argumentBag));Lines 2 and 3 both print 9. When you have a real array, spread is easier to read.
Line 5 makes an array-like object. It has numbered keys (0, 1, 2) and a length, but it is not a real array. Line 6 shows that apply accepts it too, and prints 9. Spread would throw an error here, because a plain object can't be spread into arguments.
bind makes a new function for later
STEP THROUGHbind does not run the function. It returns a new function. The new function remembers the this you gave it. It can also remember the first few arguments.
Saving some arguments now and giving the rest later has a name: partial application.
You save Ada as a contact before you need to call her. Later you tap her name, and the phone already knows who to call. A form with the first blank already filled in works the same way: you only fill in the rest.
- In real life: Save Ada as a contact
- In JavaScript:
introduce.bind(ada)keepsthisas Ada - In real life: Fill in the first blank on a form ahead of time
- In JavaScript:
multiply.bind(null, 2)saves the first argument - In real life: Tap the saved contact later
- In JavaScript: Call the new function later, for example from a timer
Step through the code below. It shows what the new function from bind can and can't do. A function made by bind is called a bound function.
Step through the facts that make bind different from call and apply.
script
return `${greeting}, I'm ${this.name}${punctuation}`;}function multiply(a, b) { return a * b;} const ada = { name: "Ada" };const grace = { name: "Grace" };const adaIntro = introduce.bind(ada, "Hello");const double = multiply.bind(null, 2); adaIntro.call(grace, "!");double === multiply;double(5);double.name;double.length; function Person(name) { this.name = name;}const BoundPerson = Person.bind({ name: "Ignored" });const student = new BoundPerson("Maya");student.name;Two lines matter most. Line 10 makes the bound function adaIntro:
introduce. bind makes a new function, adaIntro. It remembers two things for later: this is ada, and the first argument is "Hello".Line 13 calls adaIntro with call and a different object:
grace as this. The bound function ignores it and keeps ada. The "!" is still used: it is added after the saved "Hello".The code also shows a few facts about bound functions:
- A bound function is a different function from the original. Line 14 prints
false. - Line 15 runs
double(5). That really runsmultiply(2, 5), so it gives10. - Its
namestarts with"bound ". Line 16 gives"bound multiply". - Its
lengthis the number of arguments it still expects. Each saved argument takes one off. Line 17 gives1. - If you call it with
new, the new object becomesthis, and the savedthisis ignored. Lines 23 and 24 show this.
Here is partial application in a small, real task. formatPrice needs a currency sign and an amount. toFixed(2) turns a number into text with two digits after the dot.
function formatPrice(currency, amount) {
return currency + amount.toFixed(2);
}
const formatDollars = formatPrice.bind(null, "$");
console.log(formatDollars(3));Line 5 saves "$" as the first argument. formatPrice doesn't use this, so we pass null for it. Line 6 gives only the amount, 3, and prints $3.00. The part that stays the same is saved. The part that changes comes later.
Use bind when a function will be called later, for example by a timer, and it must keep its object or a fixed first argument. Don't bind every function just in case. Two other tools are sometimes clearer:
- A small wrapper function that calls the original. It is easier to read when you need extra steps around the call.
- An arrow function, when you want it to use the
thisof the code around it.
Use bind for a function you'll reuse with a fixed object or fixed first arguments. Use a wrapper function when you need extra steps around the call. Use an arrow function when you want the this of the code around it.
Borrowing a method from another object
STEP THROUGHSometimes your value doesn't have the method you need, but another kind of object does. Method borrowing means running that other method on your value, with call or apply. It works when your value has what the method needs.
Arrays get methods like slice and join from Array.prototype, the shared object that holds the methods of all arrays. You can take a method from there and run it on another value.
In the Iterating arrays lesson you met array-like values: objects with numbered items and a length, but no array methods. arguments is one of them. It is a list of all the values passed to a regular function.
Your friend's phone can make one call for you. It doesn't become your phone. You can use it because you have what a call needs: a number to dial.
- In real life: You have a phone number but no phone
- In JavaScript:
argumentshas numbered items but no array methods - In real life: Use your friend's phone once
- In JavaScript:
Array.prototype.slice.call(arguments) - In real life: Give the phone back
- In JavaScript: The method runs once; nothing is added to
arguments
Step through the code below. Each line borrows a method in a different way.
Method borrowing means running a method from one kind of value on another value that has what the method needs.
script
const args = Array.prototype.slice.call(arguments); return args.join(" | ");} const letters = Array.prototype.join.call("abc", "-");const nums = [4, 9, 2];const biggestOld = Math.max.apply(null, nums);const biggestNow = Math.max(...nums);const hasName = Object.prototype.hasOwnProperty.call({ name: "Ada" }, "name");const hasNameModern = Object.hasOwn({ name: "Ada" }, "name");collect("red", "green", "blue");const tags = { 0: "red", 1: "green", 2: "blue", length: 3 };
const joined = Array.prototype.join.call(tags, " | ");
console.log(joined);Line 1 makes an array-like object. It has numbered keys and a length, but it is not an array. Line 2 borrows join from arrays and runs it with tags as this. Line 3 prints red | green | blue.
tags is a plain object, not an array, so it has no join of its own. call runs the array's join once with tags as this. join only needs numbered items and a length, and tags has both.A borrowed method only works if your value has the properties the method reads. join reads numbered items and length, so tags works.
Newer JavaScript has easier ways to do two of these jobs:
Math.max(...nums)instead ofMath.max.apply(null, nums).Object.hasOwn(obj, key)instead ofObject.prototype.hasOwnProperty.call(obj, key). Both check whetherobjhas its own property calledkey.
You will still see the older, borrowed versions in a lot of existing code, so it helps to know how they work.
Writing your own bind
STEP THROUGHA polyfill is code that adds a built-in feature to browsers that don't have it yet. A real polyfill for bind would add a method to Function.prototype, so every function gets it. That changes every function on the page, so here we write a plain function instead: myBind(fn, thisArg, ...preset).
It takes three things:
fn: the function to bind.thisArg: the value to use asthis....preset: the arguments to save. The...collects all the remaining arguments into one array calledpreset.
function myBind(fn, thisArg, ...preset) {
return function boundFunction(...args) {
return fn.apply(thisArg, [...preset, ...args]);
};
}
function introduce(greeting, punctuation) {
return `${greeting}, I'm ${this.name}${punctuation}`;
}
const ada = { name: "Ada" };
const sayHello = myBind(introduce, ada, "Hello");
sayHello("!");In plain words, line by line:
- Line 1:
myBindtakes the function, thethisvalue, and the arguments to save. - Line 2: it returns a new function,
boundFunction. Nothing runs yet. The new function remembersfn,thisArg, andpreset. - Line 3: when
boundFunctionruns later, it callsfnwithapply. It usesthisArgasthis. It puts the saved arguments first and the new ones after.
Line 12 makes sayHello with ada and "Hello" saved. Line 13 calls it with "!" and gets "Hello, I'm Ada!". The built-in bind gives the same result.
This is a small helper. It doesn't do everything the built-in bind does.
script
return function boundFunction(...args) { return fn.apply(thisArg, [...preset, ...args]); };} function introduce(greeting, punctuation) { return `${greeting}, I'm ${this.name}${punctuation}`;} const ada = { name: "Ada" };const sayHello = myBind(introduce, ada, "Hello");sayHello("!");This helper works like the built-in bind for normal calls: a fixed this plus saved arguments. It does not handle calls with new. It also doesn't set name and length the way the built-in one does. The later Polyfill call, apply & bind lesson builds a fuller version.
Where you'll use this
You'll meet these methods in four common places. The first is timers and event handlers. You hand them a function to run later. That function is called a callback. They often call your method without its object, so this is lost. bind keeps the right object. The later Losing this lesson covers this problem in depth. Here is a first look:
const counter = {
count: 0,
increment() {
this.count += 1;
console.log(this.count);
}
};
setTimeout(counter.increment.bind(counter), 1000);setTimeout runs a function after a delay, here 1000 milliseconds (one second). Line 9 hands it a bound copy of increment. So this is still counter when it runs, and it prints 1.
The other three places:
- Saving a first argument.
const logError = console.error.bind(console, "Error:")makes a logger that always starts with"Error:". - Borrowing methods safely, especially
hasOwnProperty. - Passing an array as separate arguments with
apply. You'll see this in older code. Newer code uses spread.
Here is partial application with map. map calls a function once for each item in an array, and collects the results in a new array.
function labelTodo(prefix, todo) {
return prefix + todo;
}
const labelDone = labelTodo.bind(null, "Done: ");
console.log(JSON.stringify(["email"].map(labelDone)));Line 5 saves the first argument, "Done: ". Line 6 hands labelDone to map. map passes each todo as the next argument. It prints ["Done: email"].
- Run
introducenow withadaasthis, and"Hi"and"!"listed one by one - Run
Math.maxnow with numbers that are already in the arraynums - Hand a method to a timer so it keeps its object
- Make
doublefrommultiplyby saving2as the first argument - Turn
argumentsinto a real array withArray.prototype.slice - Pass all the arguments from one array called
args
Put each task under the method that fits it best.
Common misconceptions
- "call changes the function forever." No. It picks
thisfor one call that runs right away. The function stays the same. - "apply is an older bind." No.
applyruns the function now and takes an array.bindruns nothing. It returns a new function for later. - "bind can only set this." No. It can also save the first arguments. That is partial application.
- "You can change a bound function's this with call." No. A later
callcan add more arguments, butthisstays the bound value. - "call can set this for an arrow function." No. An arrow function uses the
thisof the code around it, andcallcan't change that. - "apply and spread always do the same thing." Both pass an array's items one by one. But
applyalso accepts array-like objects, and spread does not. - "A borrowed method works on any value." No. It only works when the value has the properties the method reads.
- "Our myBind is a complete polyfill." No. It shows the main idea: save the values, then pass them on to the real call. It leaves out
new,name, andlength.
Ask one question: does it run now? call and apply run the function now. bind makes a new function to run later.
Practice exercises
5 EXERCISESfunction introduce(greeting) {
return greeting + ", " + this.name;
}
const ada = { name: "Ada" };
console.log(introduce.call(ada, "Hi"));Hi, Adaintroduce.call(ada, "Hi") runs right away with this set to ada. So this.name is "Ada", and it prints Hi, Ada.
a-b-c from the string abc.const result = Array.prototype.join.call("abc", "-");
console.log(result);Array.prototype.join.call("abc", "-");join reads the string like a list of letters. It puts a dash between each letter and returns "a-b-c".
this is lost. Bind the method so it adds 1 to counter.count.const counter = {
count: 0,
increment() {
this.count += 1;
console.log(this.count);
}
};
const later = counter.increment.bind(counter);
later();setTimeout(counter.increment.bind(counter), 1000);bind(counter) makes a new function whose this is always counter. The timer calls it without an object, and this is still counter.
triple by saving the first argument of multiply with bind.function multiply(a, b) {
return a * b;
}
const triple = multiply.bind(null, 3);
console.log(triple(4));const triple = multiply.bind(null, 3);The saved 3 becomes a. triple(4) gives b, so the result is 3 * 4, which is 12.
bind for a normal call.function myBind(fn, thisArg, ...preset) {
return function boundFunction(...args) {
return fn.apply(thisArg, [...preset, ...args]);
};
}
function introduce(greeting, punctuation) {
return greeting + ", I'm " + this.name + punctuation;
}
const ada = { name: "Ada" };
const sayHello = myBind(introduce, ada, "Hello");
console.log(sayHello("!"));function myBind(fn, thisArg, ...preset) {
return function boundFunction(...args) {
return fn.apply(thisArg, [...preset, ...args]);
};
}The new function remembers fn, thisArg, and preset. When it runs, it calls the original function with the saved this, the saved arguments first, and the new arguments after.
Check your understanding
9 QUESTIONSQuestion 1 of 9Which method runs a function right away, sets
this, and takes the other arguments one by one?Choose an answer to see the explanation.
Question 2 of 9What does this code with
callprint?Read the code, then predictfunction show(punctuation) { console.log(this.name + punctuation); } show.call({ name: "Ada" }, "!");Choose an answer to see the explanation.
Question 3 of 9What does this code with
applyprint?Read the code, then predictfunction add(a, b) { console.log(a + b); } add.apply(null, [2, 5]);Choose an answer to see the explanation.
Question 4 of 9What does this code with an arrow function print?
Read the code, then predictfunction makeGreeter() { const greet = () => this.name; return greet; } const greetAda = makeGreeter.call({ name: "Ada" }); console.log(greetAda.call({ name: "Grace" }));Choose an answer to see the explanation.
Question 5 of 9Spread is the newer way to pass an array's items. What does this code print?
Read the code, then predictconst numbers = [4, 9, 2]; console.log(Math.max(...numbers));Choose an answer to see the explanation.
Question 6 of 9What does this code with a bound
multiplyprint?Read the code, then predictfunction multiply(a, b) { console.log(a * b); } const double = multiply.bind(null, 2); double(6);Choose an answer to see the explanation.
Question 7 of 9What does this code print when a bound function is run with
call?Read the code, then predictfunction show() { console.log(this.name); } const bound = show.bind({ name: "Ada" }); bound.call({ name: "Grace" });Choose an answer to see the explanation.
Question 8 of 9A user typed the key. Which older-style line safely checks whether
objhas its own property with that name?Choose an answer to see the explanation.
Question 9 of 9What does the small
myBindin this lesson leave out on purpose?Choose an answer to see the explanation.
Key takeaways
call(thisArg, a, b)runs the function now. You list the arguments one by one.apply(thisArg, [a, b])runs the function now. It takes the arguments from one array or array-like object.bind(thisArg, a)runs nothing. It returns a new function that remembersthisand any saved first arguments.- A later
callcan't change a bound function'sthis, and nothing can change an arrow function'sthis. - A borrowed method works when your value has the properties the method reads.
- A small
bindhelper returns a function that runsfn.apply(thisArg, [...preset, ...args]). The built-inbinddoes a little more.
Final definition: call, apply, and bind let you choose this. call and apply run the function now; bind makes a new function for later.
Up next: Arrow functions & lexical this.