¿Cuál es la diferencia entre llamar y aplicar?
¿ Cuál es la diferencia entre usar Function.prototype.apply()
e Function.prototype.call()
invocar una función?
const func = function() {
alert("Hello world!");
};
func.apply()
vs.func.call()
¿Existen diferencias de rendimiento entre los dos métodos antes mencionados? ¿ Cuándo es mejor usar call
over apply
y viceversa?
La diferencia es que apply
te permite invocar la función arguments
como una matriz; call
requiere que los parámetros se enumeren explícitamente. Un mnemotécnico útil es " A para matriz y C para coma ".
Consulte la documentación de MDN sobre solicitar y llamar .
Pseudo sintaxis:
theFunction.apply(valueForThis, arrayOfArgs)
theFunction.call(valueForThis, arg1, arg2, ...)
También existe, a partir de ES6, la posibilidad de que spread
la matriz se use con la call
función, puedes 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
K. Scott Allen tiene un buen artículo sobre el tema.
Básicamente, difieren en cómo manejan los argumentos de las funciones.
El método apply() es idéntico a call(), excepto que apply() requiere una matriz como segundo parámetro. La matriz representa los argumentos del método de destino".
Entonces:
// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
Para responder la parte sobre cuándo usar cada función, use apply
si no sabe la cantidad de argumentos que pasará, o si ya están en una matriz o en un objeto similar a una matriz (como el arguments
objeto para reenviar sus propios argumentos). Úselo call
de otra manera, ya que no es necesario envolver 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 paso ningún argumento (como en su ejemplo), prefiero call
ya que estoy llamando a la función. apply
implicaría que está aplicando la función a los argumentos (inexistentes).
No debería haber diferencias de rendimiento, excepto tal vez si usa apply
y envuelve los argumentos en una matriz (por ejemplo, f.apply(thisObject, [a, b, c])
en 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 call
sea más rápido si aún no tiene los argumentos en una matriz y apply
es más rápido si los tiene.
Aquí tienes una buena mnemónica. Aplicar utiliza matrices y siempre toma uno o dos argumentos. Cuando usas C all tienes que contar el número de argumentos.
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é.
Consulte 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 serie de argumentos, mientras que Call toma cero o más parámetros individuales. ¡Ah, ja!
.apply(this, [...])
.call(this, param1, param2, param3, param4...)
Sigue un extracto de Clausura: La guía definitiva de Michael Bolin . Puede parecer un poco extenso, pero está saturado de mucha información. Del "Apéndice B. Conceptos de JavaScript frecuentemente mal entendidos":
Qué this
se refiere cuando se llama a una función
Cuando se llama a una función de la forma foo.bar.baz()
, el objeto foo.bar
se 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, entonces 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 se ejecuta JavaScript 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
Aunque obj.addValues
y f
se refieren a la misma función, se comportan de manera diferente cuando se llaman porque el valor del receptor es diferente en cada llamada. Por esta razón, al llamar a una función que hace referencia a this
, es importante asegurarse de que this
tendrá el valor correcto cuando se llame. Para ser claros, si this
no se hiciera referencia a ellos en el cuerpo de la función, entonces 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 métodos call()
que apply()
permiten redefinir el receptor (es decir, el objeto al que this
se refiere) al llamar 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 apply()
los recibe como una única 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 y f
se obj.addValues
refieren a la misma función:
obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);
Sin embargo, dado que ni 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 this
nunca puede ser null
o undefined
cuando se llama a una función. Cuando null
o undefined
se proporciona como receptor a call()
o apply()
, el objeto global se utiliza como valor para el receptor. Por lo tanto, el código anterior tiene el mismo efecto secundario indeseable de agregar una propiedad denominada value
al objeto global.
Puede resultar ú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 this estará vinculado cuando se llame a la función en lugar de cuando se defina.
Fin del extracto.