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
argArrayesnulloundefined, entoncesreturnel resultado de llamar al método interno de[[Call]]defunc, proporcionando athisArgcomothisvalor y una lista vacía de argumentos.- Si
Type(argArray)no esObject, lanza una excepciónTypeError. - Sea
lenel resultado de llamar al método interno[[Get]]deargArraycon el argumento"length". - Sea
nToUint32(len). - Deje que
argListsea unaListvacía. - Deje que el
indexsea 0. - Repetir mientras que el
index < n - Deje que
indexNameseaToString(index). - Deje que
nextArgsea el resultado de llamar al método interno[[Get]]deargArrayconindexNamecomo argumento. -
nextArgcomo el último elemento deargList. - Establecer
indexaindex + 1. - Devuelva el resultado de llamar al
[[Call]]método interno defunc, proporcionandothisArgcomo el valor dethisyargListcomo 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
argListsea unaListvacía. - Si se llamó a este método con más de un argumento, en el orden de izquierda a derecha que comienza con
arg1agregue cada argumento como el último elemento deargList - Devuelva el resultado de llamar al
[[Call]]método interno defunc, proporcionandothisArgcomo el valor dethisyargListcomo 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é?