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 bindDecide whether to run now with listed arguments, run now with an array, or create a reusable bound function.
- 02Borrow safe methodsUse array and object methods on compatible values without trusting the target object.
- 03Write a small bind helperImplement the normal-call behavior of bind with apply, rest parameters, and spread.
Explicit this in one sentence
The previous lesson, The this keyword, introduced the four binding rules: default, implicit, explicit, and new. This lesson zooms in on explicit binding: choosing this yourself with call, apply, or bind.
Here is the core idea: call and apply run a function now with a chosen this; bind creates a new function that remembers a chosen this for later. They do not change the original function. They only decide how it is invoked.
Imagine borrowing Ada's phone to make one call. call and apply use Ada for this one call; apply takes the extra words from an array. bind is saving Ada on speed dial for later.
- In real life: Use Ada's phone for one call
- In JavaScript:
introduce.call(ada, "Hello", "!") - In real life: Use Ada's phone with contacts in 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
Where the analogy stops: A real phone can call anyone. A bound function keeps its chosen this, even after .call(other).
Function.prototype.call, apply, and bind are methods on functions. They let a normal function run with an explicit this value and, in the case of bind, optionally remember some arguments too.
You will also borrow methods from built-in objects, then write a small myBind helper. Later lessons named Currying & partial application and Polyfill call, apply & bind go deeper; here we build the accurate everyday model.
call vs apply
INTERACTIVEcall and apply are twins. Both run the function immediately. Their first argument is the value to use as this. The difference is how the remaining arguments are delivered.
| Method | Run/return | Argument style |
|---|---|---|
| call | Runs now; returns the function's result | List arguments one by one |
| apply | Runs now; returns the function's result | Pass one array-like argument bag |
| bind | Runs later; returns a new function | List preset arguments one by one |
Try the same introduce function with different target objects. The code line shown is the line you would type in a console.
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", "!");thisAdaintroduce.call(ada, "Hello", "!")call uses one chosen phone for this call. The normal arguments are listed one by one.
The important part is not where the function was written. It is how it is called. introduce.call(ada, "Hello", "!") says: “Run introduce now, and inside it make this be ada.” apply says the same thing, but it accepts ["Hello", "!"] as the argument bag. Remember a for array.
Predict each return value. call and apply both run immediately; only the argument packaging changes.
script
return `${greeting}, I'm ${this.name}${punctuation}`;} const ada = { name: "Ada" };const grace = { name: "Grace" }; introduce.call(ada, "Hello", "!");introduce.apply(grace, ["Hi", "."]);bind & partial application
STEP THROUGHbind does not run the function. It returns a new function. That new function remembers the this value you gave it. It can also remember one or more leading arguments. Saving some arguments now and supplying the rest later is called partial application.
bind is like saving Ada as a contact before you need to call. When you use it later, Ada is already chosen. A pre-filled form can also save the first value, leaving fewer values for later.
- In real life: Save Ada as the contact
- In JavaScript:
introduce.bind(ada)locksthisto Ada - In real life: Pre-fill the first blank on a form
- In JavaScript:
multiply.bind(null, 2)saves the first argument - In real life: Tap the saved contact later
- In JavaScript: The returned function can be used as a callback later
Where the analogy stops: You can edit a real contact. A bound function's this cannot be changed by call or apply.
Step through the odd 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;Notice the professional edge cases: a bound function is not identical to the original; name often starts with "bound "; length is reduced by the number of preset arguments; and if you call a bound function with new, the new object becomes this instead of the bound object.
Method borrowing
STEP THROUGHMethod borrowing means using a method from one value on another value that has a compatible shape. The Iterating arrays lesson introduced array-like values; now you can see why borrowing array methods works.
A friend's phone can make one call for you without becoming your phone. In JavaScript, arguments has numbered items and a length, so an array method that needs only those features can use it for one call.
- In real life: Your phone has a number but cannot make calls
- In JavaScript:
argumentsis indexed but lacks 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 immediately and nothing is installed on
arguments
Where the analogy stops: A borrowed phone only works with a usable number. Borrowed methods need the properties they expect.
Method borrowing is using a method from one kind of value on another compatible value.
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");Modern JavaScript often has clearer alternatives: Math.max(...nums) instead of Math.max.apply(null, nums), and Object.hasOwn(obj, key) instead of borrowing hasOwnProperty. Still, reading older code and writing defensive utilities requires understanding the borrowed-method pattern.
Writing a bind polyfill
STEP THROUGHA real polyfill would carefully match the language specification and patch Function.prototype only when needed. For a lesson, patching global prototypes in site code would be noisy, so we will write a helper function instead: myBind(fn, thisArg, ...preset).
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("!");This is a simplified helper, not a complete spec replacement for native bind.
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 simplified version matches native bind for normal calls with a fixed this and preset arguments. It does not implement constructor behavior with new, native name and length metadata, or every specification detail. The later Polyfill call, apply & bind lesson builds the fuller version.
Where you’ll use this
These methods appear most often in four places. First, event handlers and timers may call a method without its object, so bind can preserve the intended receiver. The later Losing this lesson focuses on that bug; here is the preview:
const counter = {
count: 0,
increment() {
this.count += 1;
console.log(this.count);
}
};
setTimeout(counter.increment.bind(counter), 1000);Second, utilities can partially apply a function: const logError = console.error.bind(console, "Error:"). Third, libraries borrow methods defensively, especially hasOwnProperty. Fourth, older code uses apply to turn arrays into argument lists; newer code usually uses spread.
- Run
introducenow withada,"Hi", and"!"listed separately - Run
Math.maxnow with numbers already stored innums - Hand a method to a timer without losing its object
- Create
doublefrommultiplyby saving the first argument as2 - Turn
argumentsinto a real array usingArray.prototype.slice - Use a single bag of arguments named
args
Sort each task by the best function method.
Common misconceptions
- “call changes the function forever.” No. It chooses
thisfor one immediate run. - “apply is older bind.” No.
applyruns now with an array of arguments;bindreturns a function for later. - “bind only fixes this.” It can also preset arguments, which is partial application.
- “A bound function’s this can be replaced with call.” No. A later
callcan pass more arguments, but the boundthisstays. - “Borrowed methods work on anything.” They only work when the target has the shape the method expects.
- “Our myBind is a complete polyfill.” It teaches the core forwarding idea, but skips
new,name, andlengthdetails.
Read the method name as a scheduling decision: call and apply mean “run now”; bind means “prepare a new function for 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 immediately with this === ada, so this.name is "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 an array-like value and inserts the dash separator between each character.
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) creates a callback that keeps this pointing at counter, even when the timer calls it as a plain function.
triple by binding the first argument of multiply.function multiply(a, b) {
return a * b;
}
const triple = multiply.bind(null, 3);
console.log(triple(4));const triple = multiply.bind(null, 3);The bound 3 becomes a. Calling triple(4) supplies b, so the product is 12.
bind for normal calls.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 helper stores fn, thisArg, and preset arguments in a closure. Later it calls the original function with the saved this and combined arguments.
Check your understanding
7 QUESTIONSQuestion 1 of 7Which method runs a function immediately while setting
thisand listing arguments one by one?Choose an answer to see the explanation.
Question 2 of 7What does this call snippet print?
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 7What does this apply snippet print?
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 7What does this bound multiply snippet print?
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 5 of 7What does this bound this snippet print?
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 6 of 7Which line is the safest classic property check for a key that came from a user?
Choose an answer to see the explanation.
Question 7 of 7What does the simplified
myBindin this lesson intentionally miss?Choose an answer to see the explanation.
Key takeaways
call(thisArg, ...args)runs now and lists arguments one by one.apply(thisArg, argsArray)runs now and takes arguments from one array-like value.bind(thisArg, ...preset)returns a new function that remembersthisand optional leading arguments.- Borrowing methods works when the borrowed method’s expectations match the target’s shape.
- A simplified bind helper is
fn.apply(thisArg, [...preset, ...args])inside a returned function, but nativebinddoes more.
Final definition: These function methods choose this: call and apply run immediately; bind prepares a reusable function.
Up next: Arrow functions & lexical this.