¿Cuál es la diferencia entre call y apply?

3105

¿Cuál es la diferencia entre usar calle applyinvocar una función?

var func = function() {
  alert('hello!');
};

func.apply(); vs func.call();

¿Existen diferencias de rendimiento entre los dos métodos antes mencionados? ¿Cuándo es mejor utilizar callmás applyy viceversa?

John Duff
fuente
727
Piense aen solicitar matriz de argumentos y cen solicitar columnas de argumentos.
Larry Battle
176
@LarryBattle Hago casi lo mismo, pero creo que a in request for array yc in call for comma (es decir, argumentos separados por comas).
Samih
3
Estoy de acuerdo es estúpido. Lo que es molesto es que de alguna manera esta pregunta se hace durante las entrevistas porque un grupo influyente agregó la pregunta a su lista de preguntas js importantes.
Ringo
66
Usted aplicar para un trabajo una vez (un argumento), usted [telefónicas] llamadas personas muchas veces (varios argumentos). Alternativa: hay [¿también?] Muchos juegos de Call of Duty.
Gras Double
1
Cuando la intención es invocar una función variadic con una lista de valores de argumentos independientemente de "este" valor, utilice el operador de propagación ES6, por ejemplo, fn(...input)donde input es una matriz. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Gajus

Respuestas:

3649

La diferencia es que le applypermite invocar la función argumentscomo una matriz; callrequiere que los parámetros se enumeren explícitamente. Un mnemotécnico útil es " A para una matriz y C para c omma".

Consulte la documentación de MDN sobre solicitud y llamada .

Pseudo sintaxis:

theFunction.apply(valueForThis, arrayOfArgs)

theFunction.call(valueForThis, arg1, arg2, ...)

También existe, a partir de ES6, la posibilidad de que spreadla matriz se use con la callfunción, puede ver las compatibilidades aquí .

Código de muestra:

function theFunction(name, profession) {
    console.log("My name is " + name + " and I am a " + profession +".");
}
theFunction("John", "fireman");
theFunction.apply(undefined, ["Susan", "school teacher"]);
theFunction.call(undefined, "Claude", "mathematician");
theFunction.call(undefined, ...["Matthew", "physicist"]); // used with the spread operator

línea plana
fuente
25
Una cosa para agregar es que los argumentos deben ser una matriz numérica ([]). Las matrices asociativas ({}) no funcionarán.
Kevin Schroeder
326
@KevinSchroeder: en lenguaje javascript, []se llama matriz , {}se llama objeto .
Martijn
89
A menudo solía olvidar cuál toma una matriz y cuál espera que enumeres los argumentos. Una técnica que solía recordar es que si la primera letra del método comienza con un, entonces toma una matriz, es decir, una matriz pply
aziz punjani
16
@SAM Usar una llamada en lugar de una llamada de función normal solo tiene sentido si necesita cambiar el valor de esto para la llamada de función. Un ejemplo (que convierte un objeto de argumentos de funciones en una matriz): Array.prototype.slice.call(arguments)o [].slice.call(arguments). apply tiene sentido si tiene los argumentos en una matriz, por ejemplo, en una función que llama a otra función con (casi) los mismos parámetros. Recomendación Use una llamada de función normal funcname(arg1)si eso hace lo que necesita, y guarde la llamada y solicite esas ocasiones especiales cuando realmente las necesite.
Algunos
44
@KunalSingh Ambos cally applytoma dos parámetros. El primer argumento de la apply' and función call` debe ser el objeto propietario y el segundo parámetro será una matriz o parámetros separados por comas, respectivamente. Si pasa nullo undefinedcomo primer argumento, entonces en modo no estricto, se reemplazan con un objeto global, es decirwindow
AJ Qarshi,
229

K. Scott Allen tiene una buena reseña sobre el asunto.

Básicamente, difieren en cómo manejan los argumentos de la función.

El método apply () es idéntico a call (), excepto que apply () requiere una matriz como segundo parámetro. La matriz representa los argumentos para el método de destino ".

Entonces:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
notnoop
fuente
42
El segundo parámetro de apply () y call () es opcional, no es obligatorio.
kiwi enojado
34
El primer parámetro tampoco es obligatorio.
Ikrom
@Ikrom, el primer parámetro no es obligatorio, callpero sí un requisito apply
iamcastelli
160

Para responder la parte sobre cuándo usar cada función, úsela applysi no sabe el número de argumentos que pasará, o si ya están en una matriz o en un objeto similar a una matriz (como el argumentsobjeto para reenviar sus propios argumentos. Use lo callcontrario, ya que no hay necesidad de ajustar los argumentos en una matriz.

f.call(thisObject, a, b, c); // Fixed number of arguments

f.apply(thisObject, arguments); // Forward this function's arguments

var args = [];
while (...) {
    args.push(some_value());
}
f.apply(thisObject, args); // Unknown number of arguments

Cuando no estoy pasando ningún argumento (como su ejemplo), prefiero callya que estoy llamando a la función. applyimplicaría que está aplicando la función a los argumentos (inexistentes).

No debería haber ninguna diferencia de rendimiento, excepto tal vez si usa applyy ajusta los argumentos en una matriz (por ejemplo, en f.apply(thisObject, [a, b, c])lugar de f.call(thisObject, a, b, c)). No lo he probado, por lo que podría haber diferencias, pero sería muy específico del navegador. Es probable que callsea ​​más rápido si aún no tiene los argumentos en una matriz y applymás rápido si lo tiene.

Matthew Crumley
fuente
111

Aquí hay una buena mnemónica. Una aplicación utiliza las matrices A y A siempre toma uno o dos argumentos. Cuando se utiliza C todo lo que tiene a C porte el número de argumentos.

Joe
fuente
2
¡Útil mnemotécnico allí mismo! Cambiaré el 'uno o dos argumentos' para decir 'un máximo de dos argumentos' ya que no applyse requiere ni el primer ni el segundo parámetro de . Sin embargo, no estoy seguro de por qué uno llamará applyo callsin un parámetro. Parece que alguien está tratando de averiguar por qué aquí stackoverflow.com/questions/15903782/…
dantheta
92

Si bien este es un tema antiguo, solo quería señalar que .call es un poco más rápido que .apply. No puedo decirte exactamente por qué.

Ver jsPerf, http://jsperf.com/test-call-vs-apply/3


[ UPDATE!]

Douglas Crockford menciona brevemente la diferencia entre los dos, lo que puede ayudar a explicar la diferencia de rendimiento ... http://youtu.be/ya4UHuXNygM?t=15m52s

¡Apply toma una variedad de argumentos, mientras que Call toma cero o más parámetros individuales! Ah hah!

.apply(this, [...])

.call(this, param1, param2, param3, param4...)

kmatheny
fuente
Esto depende de lo que haga la función con los parámetros / matriz, si no necesita procesar la matriz, ¿lleva menos tiempo?
Eric Hodonsky
12
Curiosamente, incluso sin la matriz, la llamada sigue siendo mucho más rápida. jsperf.com/applyvscallvsfn2
Josh Mc
@JoshMc Eso sería muy específico del navegador. En IE 11, recibo la solicitud dos veces más rápido que la llamada.
Vincent McNabb
1
1. Crear una nueva matriz significa que el recolector de basura tendrá que limpiarla en algún momento. 2. Acceder a elementos en la matriz usando la desreferencia es menos eficiente que acceder directamente a una variable (parámetro). (Creo que eso es lo que kmatheny quiso decir con "análisis", que en realidad es algo bastante diferente). Pero ninguno de mis argumentos explican el jsperf. Eso debe estar relacionado con la implementación del motor de las dos funciones, por ejemplo, tal vez crean una matriz vacía de todos modos, si no se pasó ninguna.
joeytwiddle
Gracias por compartir la prueba y el video
Gary
76

Sigue un extracto de Cierre: La guía definitiva de Michael Bolin . Puede parecer un poco largo, pero está saturado con mucha información. Del "Apéndice B. Conceptos de JavaScript frecuentemente malinterpretados":


A qué se thisrefiere cuando se llama una función

Cuando se llama a una función de la forma foo.bar.baz(), el objeto foo.barse denomina receptor. Cuando se llama a la función, es el receptor el que se utiliza como valor para this:

var obj = {};
obj.value = 10;
/** @param {...number} additionalValues */
obj.addValues = function(additionalValues) {
  for (var i = 0; i < arguments.length; i++) {
    this.value += arguments[i];
  }
  return this.value;
};
// Evaluates to 30 because obj is used as the value for 'this' when
// obj.addValues() is called, so obj.value becomes 10 + 20.
obj.addValues(20);

Si no hay un receptor explícito cuando se llama a una función, el objeto global se convierte en el receptor. Como se explica en "goog.global" en la página 47, la ventana es el objeto global cuando JavaScript se ejecuta en un navegador web. Esto lleva a un comportamiento sorprendente:

var f = obj.addValues;
// Evaluates to NaN because window is used as the value for 'this' when
// f() is called. Because and window.value is undefined, adding a number to
// it results in NaN.
f(20);
// This also has the unintentional side effect of adding a value to window:
alert(window.value); // Alerts NaN

A pesar de que obj.addValuesy fse refieren a la misma función, se comportan de manera diferente cuando se llama así porque el valor del receptor es diferente en cada llamada. Por esta razón, cuando se llama a una función que hace referencia this, es importante asegurarse de que thistendrá el valor correcto cuando se llame. Para ser claros, si thisno se hiciera referencia en el cuerpo de la función, el comportamiento de f(20)y obj.addValues(20)sería el mismo.

Como las funciones son objetos de primera clase en JavaScript, pueden tener sus propios métodos. Todas las funciones tienen los métodos call()y apply()permiten redefinir el receptor (es decir, el objeto al que se thisrefiere) cuando se llama a la función. Las firmas del método son las siguientes:

/**
* @param {*=} receiver to substitute for 'this'
* @param {...} parameters to use as arguments to the function
*/
Function.prototype.call;
/**
* @param {*=} receiver to substitute for 'this'
* @param {Array} parameters to use as arguments to the function
*/
Function.prototype.apply;

Tenga en cuenta que la única diferencia entre call()y apply()es que call()recibe los parámetros de la función como argumentos individuales, mientras que los apply()recibe como una sola matriz:

// When f is called with obj as its receiver, it behaves the same as calling
// obj.addValues(). Both of the following increase obj.value by 60:
f.call(obj, 10, 20, 30);
f.apply(obj, [10, 20, 30]);

Las siguientes llamadas son equivalentes, como fy obj.addValuesse refieren a la misma función:

obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);

Sin embargo, dado que call()ni apply()utiliza el valor de su propio receptor para sustituir el argumento del receptor cuando no está especificado, lo siguiente no funcionará:

// Both statements evaluate to NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);

El valor de thisnunca puede ser nullo undefinedcuando se llama a una función. Cuando nullo undefinedse suministra como el receptor a call()o apply(), el objeto global se usa como el valor para el receptor. Por lo tanto, el código anterior tiene el mismo efecto secundario no deseado de agregar una propiedad nombrada valueal objeto global.

Puede ser útil pensar que una función no tiene conocimiento de la variable a la que está asignada. Esto ayuda a reforzar la idea de que el valor de esto estará vinculado cuando se llama a la función en lugar de cuando se define.


Fin del extracto.

Dominykas Mostauskis
fuente
Solo para notar el hecho, eso additionalValuesno se hace referencia dentro del obj.addValuescuerpo
Viktor Stolbin
Sé que estaba respondiendo la pregunta, pero me gustaría agregar: podría haber usado bind al definir f. var f = obj.addValues;se convierte var f = obj.addValues.bind(obj) y ahora f (20) funcionaría sin tener que usar call o aplicar cada vez.
jhliberty
Sé que no lo escribiste, pero resaltaste el texto y los ejemplos del libro como relevantes, y estoy muy agradecido. Fueron de mucha ayuda.
Fralcon
34

A veces es útil que un objeto tome prestada la función de otro objeto, lo que significa que el objeto prestatario simplemente ejecuta la función prestada como si fuera la suya.

Un pequeño ejemplo de código:

var friend = {
    car: false,
    lendCar: function ( canLend ){
      this.car = canLend;
 }

}; 

var me = {
    car: false,
    gotCar: function(){
      return this.car === true;
  }
};

console.log(me.gotCar()); // false

friend.lendCar.call(me, true); 

console.log(me.gotCar()); // true

friend.lendCar.apply(me, [false]);

console.log(me.gotCar()); // false

Estos métodos son muy útiles para dar a los objetos funcionalidad temporal.

tjacks3
fuente
1
Para las personas que desean saber cómo ver, console.logconsulte: ¿Qué es console.log y cómo lo uso?
Michel Ayres
25

Otro ejemplo con Call, Apply y Bind. La diferencia entre Call y Apply es evidente, pero Bind funciona así:

  1. Bind devuelve una instancia de una función que se puede ejecutar
  2. El primer parámetro es ' esto '
  3. El segundo parámetro es una lista de argumentos separados por comas (como Llamada )

}

function Person(name) {
    this.name = name; 
}
Person.prototype.getName = function(a,b) { 
     return this.name + " " + a + " " + b; 
}

var reader = new Person('John Smith');

reader.getName = function() {
   // Apply and Call executes the function and returns value

   // Also notice the different ways of extracting 'getName' prototype
   var baseName = Object.getPrototypeOf(this).getName.apply(this,["is a", "boy"]);
   console.log("Apply: " + baseName);

   var baseName = Object.getPrototypeOf(reader).getName.call(this, "is a", "boy"); 
   console.log("Call: " + baseName);

   // Bind returns function which can be invoked
   var baseName = Person.prototype.getName.bind(this, "is a", "boy"); 
   console.log("Bind: " + baseName());
}

reader.getName();
/* Output
Apply: John Smith is a boy
Call: John Smith is a boy
Bind: John Smith is a boy
*/
Mahesh
fuente
23

Me gustaría mostrar un ejemplo, donde se usa el argumento 'valueForThis':

Array.prototype.push = function(element) {
   /*
   Native code*, that uses 'this'       
   this.put(element);
   */
}
var array = [];
array.push(1);
array.push.apply(array,[2,3]);
Array.prototype.push.apply(array,[4,5]);
array.push.call(array,6,7);
Array.prototype.push.call(array,8,9);
//[1, 2, 3, 4, 5, 6, 7, 8, 9] 

** detalles: http://es5.github.io/#x15.4.4.7 *


fuente
20

Call () toma argumentos separados por comas, por ejemplo:

.call(scope, arg1, arg2, arg3)

y apply () toma una serie de argumentos, por ejemplo:

.apply(scope, [arg1, arg2, arg3])

Aquí hay algunos ejemplos de uso más: http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/

Mark Karwowski
fuente
`// call () === argumentos separados por comas (argumentos-list) .call (this, args1, args2, args3, ...) // apply () === conjunto de argumentos (array-items). apply (this, [arr0, arr1, arr2, ...]) `
xgqfrms
19

De los documentos de MDN en Function.prototype.apply () :

El método apply () llama a una función con un thisvalor dado y argumentos proporcionados como una matriz (o un objeto similar a una matriz).

Sintaxis

fun.apply(thisArg, [argsArray])

De los documentos de MDN en Function.prototype.call () :

El método call () llama a una función con un thisvalor dado y argumentos proporcionados individualmente.

Sintaxis

fun.call(thisArg[, arg1[, arg2[, ...]]])

Desde Function.apply y Function.call en JavaScript :

El método apply () es idéntico a call (), excepto que apply () requiere una matriz como segundo parámetro. La matriz representa los argumentos para el método de destino.


Ejemplo de código:

var doSomething = function() {
    var arr = [];
    for(i in arguments) {
        if(typeof this[arguments[i]] !== 'undefined') {
            arr.push(this[arguments[i]]);
        }
    }
    return arr;
}

var output = function(position, obj) {
    document.body.innerHTML += '<h3>output ' + position + '</h3>' + JSON.stringify(obj) + '\n<br>\n<br><hr>';
}

output(1, doSomething(
    'one',
    'two',
    'two',
    'one'
));

output(2, doSomething.apply({one : 'Steven', two : 'Jane'}, [
    'one',
    'two',
    'two',
    'one'
]));

output(3, doSomething.call({one : 'Steven', two : 'Jane'},
    'one',
    'two',
    'two',
    'one'
));

Ver también este violín .

John Slegers
fuente
10

Aquí hay una publicación pequeña, escribí sobre esto:

http://sizeableidea.com/call-versus-apply-javascript/

var obj1 = { which : "obj1" },
obj2 = { which : "obj2" };

function execute(arg1, arg2){
    console.log(this.which, arg1, arg2);
}

//using call
execute.call(obj1, "dan", "stanhope");
//output: obj1 dan stanhope

//using apply
execute.apply(obj2, ["dan", "stanhope"]);
//output: obj2 dan stanhope

//using old school
execute("dan", "stanhope");
//output: undefined "dan" "stanhope"
Dan
fuente
Aquí hay otro: blog.i-evaluation.com/2012/08/15/javascript-call-and-apply pero básicamente es correcto: .call (alcance, arg1, arg2, arg3)
Mark Karwowski
7

La diferencia es que call()toma los argumentos de la función por separado y apply()toma los argumentos de la función en una matriz.

Sanjib Debnath
fuente
6

Podemos diferenciar los métodos de llamada y aplicación de la siguiente manera

LLAMADA: Una función con argumento proporciona individualmente. Si conoce los argumentos a pasar o no hay argumentos para pasar, puede usar call.

APLICAR: llama a una función con el argumento proporcionado como una matriz. Puede usar apply si no sabe cuántos argumentos pasarán a la función.

Hay una ventaja de usar apply over call, no necesitamos cambiar el número de argumentos solo podemos cambiar una matriz que se pasa.

No hay gran diferencia en el rendimiento. Pero podemos decir que la llamada es un poco más rápida en comparación con la aplicación porque una matriz necesita evaluar en el método de aplicación.

Praveen D
fuente
5

La diferencia entre estos métodos es la forma en que desea pasar los parámetros.

"A para matriz y C para coma" es una práctica mnemónica.

venkat7668
fuente
11
¿Qué proporciona esta respuesta que aún no está bien proporcionada en otras respuestas?
Kyll
5

Llamar y aplicar ambos se utilizan para forzar el thisvalor cuando se ejecuta una función. La única diferencia es que calltoma n+1argumentos donde 1 es thisy 'n' arguments. applytoma solo dos argumentos, uno es thisel otro es una matriz de argumentos.

La ventaja que veo applymás arriba calles que podemos delegar fácilmente una llamada a otra función sin mucho esfuerzo;

function sayHello() {
  console.log(this, arguments);
}

function hello() {
  sayHello.apply(this, arguments);
}

var obj = {name: 'my name'}
hello.call(obj, 'some', 'arguments');

Observar la facilidad con que delegamos helloa sayHellousar apply, pero con callesto es muy difícil de lograr.

Raghavendra
fuente
4

Aunque callyapply logre lo mismo, creo que hay al menos un lugar donde no se puede usar, callpero solo se puede usar apply. Es entonces cuando desea admitir la herencia y desea llamar al constructor.

Aquí hay una función que le permite crear clases que también admite la creación de clases extendiendo otras clases.

function makeClass( properties ) {
    var ctor = properties['constructor'] || function(){}
    var Super = properties['extends'];
    var Class = function () {
                 // Here 'call' cannot work, only 'apply' can!!!
                 if(Super)
                    Super.apply(this,arguments);  
                 ctor.apply(this,arguments);
                }
     if(Super){
        Class.prototype = Object.create( Super.prototype );
        Class.prototype.constructor = Class;
     }
     Object.keys(properties).forEach( function(prop) {
           if(prop!=='constructor' && prop!=='extends')
            Class.prototype[prop] = properties[prop];
     });
   return Class; 
}

//Usage
var Car = makeClass({
             constructor: function(name){
                         this.name=name;
                        },
             yourName: function() {
                     return this.name;
                   }
          });
//We have a Car class now
 var carInstance=new Car('Fiat');
carInstance.youName();// ReturnsFiat

var SuperCar = makeClass({
               constructor: function(ignore,power){
                     this.power=power;
                  },
               extends:Car,
               yourPower: function() {
                    return this.power;
                  }
              });
//We have a SuperCar class now, which is subclass of Car
var superCar=new SuperCar('BMW xy',2.6);
superCar.yourName();//Returns BMW xy
superCar.yourPower();// Returns 2.6
Dhana Krishnasamy
fuente
Creo que la llamada funcionaría allí con el operador de propagación como se describe en la respuesta seleccionada. A menos que me falte algo.
jhliberty
4

Resumen:

Ambos call()y apply()son métodos que se encuentran en Function.prototype. Por lo tanto, están disponibles en cada objeto de función a través de la cadena de prototipo. Ambos call()y apply()pueden ejecutar una función con un valor especificado dethis .

La principal diferencia entre call()y apply()es la forma en que tienes que pasarle argumentos. En ambos call()y apply()pasa como primer argumento el objeto que desea que sea el valor this. Los otros argumentos difieren de la siguiente manera:

  • Con call() usted tiene que poner los argumentos normalmente (a partir del segundo argumento)
  • Con apply()usted tiene que pasar en una variedad de argumentos.

Ejemplo:

let obj = {
  val1: 5,
  val2: 10
}

const summation = function (val3, val4) {
  return  this.val1 + this.val2 + val3 + val4;
}

console.log(summation.apply(obj, [2 ,3]));
// first we assign we value of this in the first arg
// with apply we have to pass in an array


console.log(summation.call(obj, 2, 3));
// with call we can pass in each arg individually

¿Por qué necesitaría usar estas funciones?

El thisvalor puede ser complicado a veces en javascript. El valor de thisdeterminado cuando se ejecuta una función, no cuando se define una función. Si nuestra función depende de un thisenlace correcto , podemos usar call()y apply()hacer cumplir este comportamiento. Por ejemplo:

var name = 'unwantedGlobalName';

const obj =  {
  name: 'Willem',
  sayName () { console.log(this.name);}
}


let copiedMethod = obj.sayName;
// we store the function in the copiedmethod variable



copiedMethod();
// this is now window, unwantedGlobalName gets logged

copiedMethod.call(obj);
// we enforce this to be obj, Willem gets logged

Willem van der Veen
fuente
4

La principal diferencia es que, usando call, podemos cambiar el alcance y pasar argumentos de manera normal, pero aplicar le permite llamarlo usando argumentos como una matriz (pasarlos como una matriz). Pero en términos de lo que deben hacer en su código, son bastante similares.

Si bien la sintaxis de esta función es casi idéntica a la de apply (), la diferencia fundamental es que call () acepta una lista de argumentos, mientras que apply () acepta una sola matriz de argumentos.

Como puede ver, no hay una gran diferencia, pero aún así, hay casos que preferimos usar call () o apply (). Por ejemplo, mire el código a continuación, que busca el número más pequeño y más grande en una matriz de MDN, utilizando el método de aplicación:

// min/max number in an array
var numbers = [5, 6, 2, 3, 7];

// using Math.min/Math.max apply
var max = Math.max.apply(null, numbers); 
// This about equal to Math.max(numbers[0], ...)
// or Math.max(5, 6, ...)

var min = Math.min.apply(null, numbers)

Entonces, la principal diferencia es la forma en que pasamos los argumentos:

Llamada:

function.call(thisArg, arg1, arg2, ...);

Aplicar:

function.apply(thisArg, [argsArray]);
Alireza
fuente
2

Permítanme agregar un pequeño detalle a esto.

Estas dos llamadas son casi equivalentes:

func.call(context, ...args); // pass an array as list with spread operator

func.apply(context, args);   // is same as using apply

Solo hay una pequeña diferencia:

  • El spreadoperador ... permite pasar iterable args como la lista para llamar.
  • El applyacepta sólo de matriz-como args.

Entonces, estas llamadas se complementan entre sí. Donde esperamos un iterables , callobras, donde se espera una matriz similar , applyobras.

Y para los objetos que son a la vez iterable y gama-como , como un conjunto de bienes, que técnicamente podríamos utilizar cualquiera de ellos, pero aplicable será probablemente más rápido porque la mayoría de los motores de JavaScript internamente optimizar mejor.

Pravin Divraniya
fuente