javascript - tos - venenos que no se detectan en una autopsia
¿Por qué la llamada es mucho más rápida que aplicar? (1)
Referencia a la edición 5.1 de la especificación de lenguaje ECMAScript (junio de 2011) :
15.3.4.3 Function.prototype.apply (thisArg, argArray)
Cuando se llama al método de apply
en una función de objeto con los argumentos thisArg y argArray, se toman los siguientes pasos:
Si
IsCallable(func)
esfalse
, lance una excepciónTypeError
.Si
argArray
esnull
oundefined
, entoncesreturn
el resultado de llamar al método interno de[[Call]]
defunc
, proporcionando athisArg
comothis
valor y una lista vacía de argumentos.- Si
Type(argArray)
no esObject
, lanza una excepciónTypeError
. - Sea
len
el resultado de llamar al método interno[[Get]]
deargArray
con el argumento"length"
. - Sea
n
ToUint32(len)
. - Deje que
argList
sea unaList
vacía. - Deje que el
index
sea 0. - Repetir mientras que el
index < n
- Deje que
indexName
seaToString(index)
. - Deje que
nextArg
sea el resultado de llamar al método interno[[Get]]
deargArray
conindexName
como argumento. -
nextArg
como el último elemento deargList
. - Establecer
index
aindex + 1
. - Devuelva el resultado de llamar al
[[Call]]
método interno defunc
, proporcionandothisArg
como el valor dethis
yargList
como la lista de argumentos.
15.3.4.4 Function.prototype.call (thisArg [, arg1 [, arg2,…]])
Cuando se call
método de llamada en una función de objeto con el argumento thisArg y los argumentos opcionales arg1, arg2, etc., se toman los siguientes pasos:
- Si
IsCallable(func)
esfalse
, lance una excepciónTypeError
. - Deje que
argList
sea unaList
vacía. - Si se llamó a este método con más de un argumento, en el orden de izquierda a derecha que comienza con
arg1
agregue cada argumento como el último elemento deargList
- Devuelva el resultado de llamar al
[[Call]]
método interno defunc
, proporcionandothisArg
como el valor dethis
yargList
como la lista de argumentos.
Como podemos ver, el formato en el que se especifica la aplicación es notablemente más pesado y necesita hacer mucho más debido a la necesidad de cambiar el formato en el que se dan los argumentos y cómo se necesitan finalmente.
Hay una serie de comprobaciones que no son necesarias en la call
debido a la diferencia de formato de entrada.
Otro punto clave es la manera en que los argumentos se repiten (pasos 4-12 en apply
, implícito en el paso 3 de call
): toda la configuración para el looping se ejecuta en apply
sin importar cuántos argumentos haya en realidad, en call
Todo esto se hace solo si es necesario.
Además, vale la pena señalar que no se especifica la forma en que se implementa el paso 3 en la call
, lo que ayudaría a explicar las diferencias drásticas en el comportamiento del navegador diferente.
Entonces, para resumir brevemente: la call
es más rápida de lo que se apply
porque los parámetros de entrada ya tienen el formato necesario para el método interno.
Asegúrese de leer los comentarios a continuación para una mayor discusión.
Me pregunto si alguien sabe por qué la call
es mucho más rápida que apply
. En Chrome, es aproximadamente 4 veces más rápido y apply2
, e incluso puedo hacer un prototipo personalizado, apply2
, que (en la mayoría de los casos) se ejecuta 2x tan rápido como se apply
(la idea se toma de angular):
Function.prototype.apply2 = function( self, arguments ){
switch( arguments.length ){
case 1: this.call( self, arguments[0] ); break;
case 2: this.call( self, arguments[0], arguments[1] ); break;
case 3: this.call( self, arguments[0], arguments[1], arguments[2] ); break;
case 4: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3] ); break;
case 5: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4] ); break;
case 6: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5] ); break;
case 7: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6] ); break;
case 8: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7] ); break;
case 9: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8] ); break;
case 10: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9] ); break;
default: this.apply( self, arguments ); break;
}
};
Entonces, ¿alguien sabe por qué?