Real difference between Call and Apply methods

4

What is the difference between the call and apply methods? I could not identify the difference in which one separates the arguments and the other generates a vector for the arguments:

function scope() {
        console.log(this, arguments.length);
    }
    scope() // window, 0
    scope.call("foobar", [ 1, 2 ]); // "foobar", 1
    scope.apply("foobar", [ 1, 2 ]); // "foobar", 2
    
asked by anonymous 14.04.2018 / 22:06

2 answers

2

The difference is in the parameter they use. While apply will pass to the function an array , call will pass individual values separated by commas :

Your example shows this:

scope.call("foobar", [ 1, 2 ]);
                     --------
                         ↑
                apenas 1 parâmetro
               arguments.length = 1


scope.apply("foobar", [ 1, 2 ]);
                      --------
                          ↑
                1 array com 2 itens
               arguments.length = 2

Another example of call :

scope.call("foobar", [ 1, 2 ], 3);
                     --------  -
                         ↑     ↑
                      2 parâmetros
                   arguments.length = 2

The apply does not generate a vector, it sends a vector as the second parameter to the function. Example:

function scope() {
   for(var item of arguments){
      console.log(item);
   }
}
scope.apply("foobar", ['abc','def','ghi']);

MDN documentation on apply :

  

Note: While the syntax of this function is almost identical to that of call() , the fundamental difference is that call() accepts an    argument list , while apply() accepts single array of arguments .

Free translation:

  

Note: Although the syntax of this function is almost the same as call() , the fundamental difference is that call() accepts a    argument list , whereas apply() accepts a simple array with arguments .

The same example above using call :

function scope() {
   for(var item of arguments){
      console.log(item);
   }
}
scope.call("foobar", ['abc','def','ghi']);

Note that the ['abc','def','ghi'] argument arrives at the function with an integer array. Unlike apply , which sends each vector item as a separate argument.

    
14.04.2018 / 22:33
3

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:

14.04.2018 / 23:47