Unfortunately, your example was not the best. I start by first changing the example to show arguments
completely and not just length
:
function scope() {
console.log(this, JSON.stringify(arguments));
}
scope.call("foobar", [ 1, 2 ]); // "foobar", 1
scope.apply("foobar", [ 1, 2 ]); // "foobar", 2
Notice the difference between the two.
In the case of call
arguments
are:
{"0":[1,2]}
That is, 1
argument is an array [1,2]
, but not apply
we see:
{"0":1,"1":2}
What are 2
arguments, the first is 1
and the second is 2
.
That is, in call
the array is all passed as a single parameter, but in apply
the array indicates the parameters to be passed as if you had written them manually.
Then in your case do:
scope.apply("foobar", [1,2]);
It would be equivalent to doing:
foobar(1 , 2);
Further expanding it, do:
scope.apply("foobar", [1,2,3,4,5]);
It would correspond to:
foobar(1 , 2, 3, 4, 5);
So we see that it's different from calling the function with call
.
Real Scenarios
Apply
It is useful when you have an array of elements to pass to a function, but this function does not expect an array but separate elements and written directly.
A clear case of this is Math.max
to calculate the maximum of several numbers:
const arr = [1, 2, 3 ,4, 5];
console.log(Math.max(arr));
console.log(Math.max.apply(null, arr));
Math.max(arr)
does not work because max
does not support receiving an array directly. Math.max
is waiting to receive the loose elements, like this:
Math.max(1, 2, 3, 4, 5)
That's exactly what apply
does. Also notice that I indicated null
as the first parameter which is the parameter that will serve as this
in the function, and in this case we do not want any this
.
Call
The call is common to be used in "old" Javascript style inheritance without the reserved words class
and super
.
I'll take the example of MDN, which certainly may not be the best, but it's still used exactly in this context. Imagine that you have a Produto
function to construct products and then have another one for Comida
where Comida
would be an extension of Produto
.
The call to Produto
within the Comida
constructor would normally be done with call
:
function Produto(nome, preco) {
this.nome = nome;
this.preco = preco;
}
function Comida(nome, preco) {
Produto.call(this, nome, preco);
this.categoria = 'comida';
}
let queijo = new Comida('feta', 5);
console.log(queijo);
Documentation: