What's the difference between `.call` and `.apply` in JavaScript?
TL;DR
.call
and .apply
are both used to invoke functions with a specific this
context and arguments. The primary difference lies in how they accept arguments:
.call(thisArg, arg1, arg2, ...)
: Takes arguments individually..apply(thisArg, [argsArray])
: Takes arguments as an array.
Assuming we have a function add
, the function can be invoked using .call
and .apply
in the following manner:
function add(a, b) {return a + b;}console.log(add.call(null, 1, 2)); // 3console.log(add.apply(null, [1, 2])); // 3
Call vs Apply
Both .call
and .apply
are used to invoke functions and the first parameter will be used as the value of this
within the function. However, .call
takes in comma-separated arguments as the next arguments while .apply
takes in an array of arguments as the next argument.
An easy way to remember this is C for call
and comma-separated and A for apply
and an array of arguments.
function add(a, b) {return a + b;}console.log(add.call(null, 1, 2)); // 3console.log(add.apply(null, [1, 2])); // 3
With ES6 syntax, we can invoke call
using an array along with the spread operator for the arguments.
function add(a, b) {return a + b;}console.log(add.call(null, ...[1, 2])); // 3
Use cases
Context management
.call
and .apply
can set the this
context explicitly when invoking methods on different objects.
const person = {name: 'John',greet() {console.log(`Hello, my name is ${this.name}`);},};const anotherPerson = { name: 'Alice' };person.greet.call(anotherPerson); // Hello, my name is Aliceperson.greet.apply(anotherPerson); // Hello, my name is Alice
Function borrowing
Both .call
and .apply
allow borrowing methods from one object and using them in the context of another. This is useful when passing functions as arguments (callbacks) and the original this
context is lost. .call
and .apply
allow the function to be invoked with the intended this
value.
function greet() {console.log(`Hello, my name is ${this.name}`);}const person1 = { name: 'John' };const person2 = { name: 'Alice' };greet.call(person1); // Hello, my name is Johngreet.call(person2); // Hello, my name is Alice
Alternative syntax to call methods on objects
.apply
can be used with object methods by passing the object as the first argument followed by the usual parameters.
const arr1 = [1, 2, 3];const arr2 = [4, 5, 6];Array.prototype.push.apply(arr1, arr2); // Same as arr1.push(4, 5, 6)console.log(arr1); // [1, 2, 3, 4, 5, 6]
Deconstructing the above:
- The first object,
arr1
will be used as thethis
value. .push()
is called onarr1
usingarr2
as arguments as an array because it's using.apply()
.Array.prototype.push.apply(arr1, arr2)
is equivalent toarr1.push(...arr2)
.
It may not be obvious, but Array.prototype.push.apply(arr1, arr2)
causes modifications to arr1
. It's clearer to call methods using the OOP-centric way instead where possible.
Follow-Up Questions
- How do
.call
and.apply
differ fromFunction.prototype.bind
?
Practice
Practice implementing your own Function.prototype.call
method and Function.prototype.apply
method on GreatFrontEnd.