Recently, I had the issue that I was confronted with JavaScript's call
and apply
. Since I forgot where the difference is or why you may use it, I learned it.
Generally, both functions are a prototype of any JavaScript Function
. This means, you can invoke any function-object with call
and apply
. With "function-object" I mean a function as an object, so without the ()
).
In short, call
and apply
are both used to invoke functions, where apply takes function parameters (arguments) as a single array, and call as an endless list of arguments.
Example
A code example always helps to understand things. The following example shows a very simple function that takes two arguments: a
and b
. The function logs their sum, as well as its this
context.
function add(a, b) { | |
console.log(a + b); | |
console.log(this); | |
} | |
add(1, 2); | |
// ⇒ 3 | |
// ⇒ Object [global] { global: [Circular], clearInterval ... } | |
add.apply({ example: "test" }, [1, 2]); | |
// ⇒ 3 | |
// ⇒ { example: "test" } | |
add.call(null /* or undefined */, 1, 2); | |
// ⇒ 3 | |
// ⇒ Object [global] { global: [Circular], clearInterval ... } |
In the very first and last of the three calls, this
is the global
object. It depends on where you execute it, here it was in a node environment. If you would try this in a browser, you would probably see the the window
object.
Now, with the call
call in line 10, you actually call the function with { example: "test" }
as the this
object. As the second argument, you pass the function arguments as a single array [1, 2]
. The apply
call in line 14 does not pass any this
object, leading to no context change. There, the arguments are passed as single objects (1, 2
).
In relation to call
, you could also use JavaScript's bind
in order to create a new function with a different this
context. It takes the same arguments and causes the same outcome as with call
: add.bind({ example: 'test' }, 1, 2)
.
A Bigger Example
The following example shows the usage of call
and apply
in a more complex and realistic scenario. What the code does is printing the name
and age
of its this
context, and additionally prints some extra information passed as an argument.
const Example = { | |
name: "Hans", | |
age: 42, | |
print: function() { | |
console.log(`Name: "${this.name}", age: "${this.age}".`); | |
}, | |
printSomething: function(extra) { | |
console.log(`Extra: ${extra}.`); | |
} | |
}; | |
Example.print(); | |
// ⇒ Name: "Hans", age: "42". | |
Example.print.apply(null /* or undefined */, []); | |
// ⇒ Name: "undefined", age: "undefined". | |
Example.print.call(null /* or undefined */, []); | |
// ⇒ Name: "undefined", age: "undefined". | |
Example.print.apply(Example, []); | |
// ⇒ Name: "Hans", age: "42". | |
Example.print.apply({ name: "Marius", age: 15 }, []); | |
// ⇒ Name: "Marius", age: "15". | |
Example.printSomething.apply(null, [ '"@reime005"' ]); | |
// ⇒ Extra: "@reime005". | |
Example.printSomething.call(null, '"@reime005"', "not used"); | |
// ⇒ Extra: "@reime005". |
Now, the first call in line 12 is the way most are probably used to. It prints its defined values. Now, line 15 overrides the this
object with the global
object, leading to undefined
. Line 24 is also interesting, in which the object { name: "Marius", age: 15 }
is passed, leading to a different outcome.
Conclusion
JavaScript functions can be called with apply
and call
. Both can change its function context as the first argument. The only difference between them is the syntax of the second (and following) argument(s). The reason why you use one or another (or at all) lies in the nature of JavaScript, reusing code, code privacy (hiding variables) or just a personal preference.