2008-09-17 22 views
72

Me he estado preguntando, ¿existe una diferencia de rendimiento entre el uso de funciones con nombre y funciones anónimas en Javascript?¿El uso de funciones anónimas afecta el rendimiento?

for (var i = 0; i < 1000; ++i) { 
    myObjects[i].onMyEvent = function() { 
     // do something 
    }; 
} 

vs

function myEventHandler() { 
    // do something 
} 

for (var i = 0; i < 1000; ++i) { 
    myObjects[i].onMyEvent = myEventHandler; 
} 

La primera es más ordenado, ya que no saturar el código con funciones que se utilizan raramente, pero qué importa que usted está re-declarar que la función varias veces?

+0

Sé que no está en la pregunta, pero en lo que respecta a la limpieza/legibilidad del código, creo que el "camino correcto" está en el medio. El "desorden" de las funciones de alto nivel que se usan raramente es molesto, pero también lo es el código fuertemente anidado que depende mucho de las funciones anónimas que se declaran en línea con su invocación (pensemos en node.js callback hell). Tanto el primero como el último pueden dificultar el seguimiento de la depuración/ejecución. –

+0

Las pruebas de rendimiento a continuación ejecutan la función para miles de iteraciones. Incluso si ve una diferencia sustancial, la mayoría de los casos de uso no lo harán en iteraciones de ese orden. Por lo tanto, es mejor elegir lo que se adapte a sus necesidades e ignorar el rendimiento para este caso particular. – Medorator

+0

@nickf, por supuesto, es una pregunta demasiado antigua, pero vea la nueva respuesta actualizada –

Respuesta

74

El problema de rendimiento aquí es el costo de crear un nuevo objeto de función en cada iteración del bucle y no el hecho de que utiliza un anónimo función:

for (var i = 0; i < 1000; ++i) {  
    myObjects[i].onMyEvent = function() { 
     // do something  
    }; 
} 

Usted está creando una función distinta mil objetos a pesar de que tienen el mismo cuerpo de código y no vinculante para el ámbito léxico (closure). El siguiente parece más rápido, por otro lado, porque simplemente asigna el misma referencia función a los elementos de la matriz a lo largo del bucle:

function myEventHandler() { 
    // do something 
} 

for (var i = 0; i < 1000; ++i) { 
    myObjects[i].onMyEvent = myEventHandler; 
} 

Si se va a crear la función anónima antes de entrar en el bucle, entonces sólo asignar referencias a ella a los elementos de la matriz, mientras que dentro del bucle, se dará cuenta de que no hay rendimiento o diferencia semántica en absoluto cuando se compara con la versión función llamada:

var handler = function() { 
    // do something  
}; 
for (var i = 0; i < 1000; ++i) {  
    myObjects[i].onMyEvent = handler; 
} 

En resumen, no hay costo de rendimiento observable usando funciones anónimas sobre nombradas.

Como un aparte, puede aparecer desde arriba que no hay diferencia entre:

function myEventHandler() { /* ... */ } 

y:

var myEventHandler = function() { /* ... */ } 

El primero es una declaración función mientras que el último es una variable asignación a una función anónima. Aunque parezcan tener el mismo efecto, JavaScript los trata de forma ligeramente diferente. Para entender la diferencia, recomiendo leer, "JavaScript function declaration ambiguity".

El tiempo de ejecución real de cualquier enfoque dependerá en gran medida de la implementación del compilador y el tiempo de ejecución del navegador. Para una comparación completa del rendimiento moderno del navegador, visite the JS Perf site

+0

Olvidó los paréntesis antes del cuerpo de la función. Acabo de probarlo, se requieren. –

+0

@chinoto Gracias por señalar la omisión. ¡Fijo! –

+0

¡parece que los resultados del benchmark dependen mucho del motor js! – aleclofabbro

2

Como principio de diseño general, debe evitar la implimentación del mismo código varias veces. En su lugar, debe extraer el código común en una función y ejecutar esa función (general, bien probada, fácil de modificar) desde múltiples lugares.

Si (a diferencia de lo que se infiere de su pregunta) que está declarando la función interna de una vez usando ese código una vez (y no tienen nada más idénticos en su programa), entonces una función anonomous probablemente (eso es una gente supongo) obtiene tratado de la misma manera por el compilador como una función nombrada normal.

Es una característica muy útil en casos específicos, pero no debe usarse en muchas situaciones.

1

No esperaría mucha diferencia, pero si hay una, es probable que varíe según el motor de secuencias de comandos o el navegador.

Si encuentra que el código es más fácil de asimilar, el rendimiento no es un problema a menos que espere llamar a la función millones de veces.

0

Lo que sin duda hará que el bucle más rápido a través de una variedad de navegadores, especialmente los navegadores Internet Explorer, es un bucle de la siguiente manera:

for (var i = 0, iLength = imgs.length; i < iLength; i++) 
{ 
    // do something 
} 

Usted ha puesto de forma arbitraria 1000 en la condición de bucle, pero se obtiene mi deriva si quieres ver todos los elementos de la matriz.

20

Aquí está mi código de prueba:

var dummyVar; 
function test1() { 
    for (var i = 0; i < 1000000; ++i) { 
     dummyVar = myFunc; 
    } 
} 

function test2() { 
    for (var i = 0; i < 1000000; ++i) { 
     dummyVar = function() { 
      var x = 0; 
      x++; 
     }; 
    } 
} 

function myFunc() { 
    var x = 0; 
    x++; 
} 

document.onclick = function() { 
    var start = new Date(); 
    test1(); 
    var mid = new Date(); 
    test2(); 
    var end = new Date(); 
    alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid)); 
} 

Los resultados:
Prueba 1: 142ms Prueba 2: 1983ms

Parece ser que el motor de JS no reconoce que se trata de la misma función en Test2 y lo compila cada vez.

+3

¿En qué navegador se realizó esta prueba? – andnil

+5

Veces para mí en Chrome 23: (2ms/17ms), IE9: (20ms/83ms), FF 17: (2ms/96ms) – Davy8

+0

Su respuesta merece más peso. Mis tiempos en Intel i5 4570S: Chrome 41 (1/9), IE11 (1/25), FF36 (1/14). Claramente, la función anónima en un ciclo funciona peor. – ThisClark

0

@nickf

Eso es una prueba más fatua sin embargo, usted está comparando el tiempo de ejecución y compilación allí, que es, obviamente, va a costar el método 1 (compila N veces, dependiendo del motor JS) con el método 2 (compila una vez). No me puedo imaginar a un desarrollador de JS que pase su código de escritura de prueba de esa manera.

Un enfoque mucho más realista es la asignación anónima, ya que de hecho está utilizando para su método document.onclick es más como el siguiente, que de hecho favorece levemente el método anon.

El uso de un marco de pruebas similares a la suya:


function test(m) 
{ 
    for (var i = 0; i < 1000000; ++i) 
    { 
     m(); 
    } 
} 

function named() {var x = 0; x++;} 

var test1 = named; 

var test2 = function() {var x = 0; x++;} 

document.onclick = function() { 
    var start = new Date(); 
    test(test1); 
    var mid = new Date(); 
    test(test2); 
    var end = new Date(); 
    alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms"); 
} 
0

una referencia casi siempre va a ser más lenta de lo que se refiere. Piénsalo de esta manera - digamos que desea imprimir el resultado de sumar 1 + 1. ¿Cuál tiene más sentido:

alert(1 + 1); 

o

a = 1; 
b = 1; 
alert(a + b); 

Soy consciente de que es una manera muy simplista de mirar pero es ilustrativo, ¿no? Utilice una referencia sólo si se va a utilizar varias veces - por ejemplo, ¿cuál de estos ejemplos tiene más sentido:

$(a.button1).click(function(){alert('you clicked ' + this);}); 
$(a.button2).click(function(){alert('you clicked ' + this);}); 

o

function buttonClickHandler(){alert('you clicked ' + this);} 
$(a.button1).click(buttonClickHandler); 
$(a.button2).click(buttonClickHandler); 

La segunda es una mejor práctica, incluso si se tiene más líneas Espero que todo esto sea útil. (y la sintaxis de jquery no arrojó a nadie)

0

¡SÍ!Las funciones anónimas son más rápidas que las funciones regulares. Quizás si la velocidad es de suma importancia ... más importante que la reutilización del código, entonces considere usar funciones anónimas.

Hay un muy buen artículo sobre la optimización de JavaScript y funciones anónimas aquí:

http://dev.opera.com/articles/view/efficient-javascript/?page=2

0

@nickf

(deseo que tenía el representante de comentar solo, pero sólo acaba de encontrar este sitio)

Mi punto es que aquí hay confusión entre las funciones nombradas/anónimas y el caso de uso de ejecutar + compilar en una iteración. Como lo ilustré, la diferencia entre anon + named es insignificante en sí misma; digo que es el caso de uso el que está defectuoso.

Me parece obvio, pero si no creo que el mejor consejo es "no hagas tonterías" (de las cuales el cambio constante de bloque + creación de objeto de este caso de uso es uno) y si no eres seguro, prueba!

1

Los objetos anónimos son más rápidos que los objetos nombrados. Pero llamar a más funciones es más costoso, y en un grado que eclipsa cualquier ahorro que pueda obtener al usar funciones anónimas. Cada función llamada se agrega a la pila de llamadas, lo que introduce una pequeña pero no trivial cantidad de gastos generales.

Pero a menos que esté escribiendo rutinas de cifrado/descifrado o algo similar sensible al rendimiento, como muchos otros han señalado, siempre es mejor optimizar el código elegante y fácil de leer sobre código rápido.

Suponiendo que está escribiendo un código bien estructurado, los problemas de velocidad deberían ser responsabilidad de los que escriben los intérpretes/compiladores.

1

Donde podemos tener un impacto en el rendimiento es en la operación de declarar funciones. Aquí es un punto de referencia de declarar funciones dentro del contexto de otra función o fuera:

http://jsperf.com/function-context-benchmark

En Chrome la operación es más rápida si declaramos la función exterior, pero en Firefox es todo lo contrario.

En otro ejemplo, vemos que si la función interna no es una pura función, tendrá una falta de rendimiento también en Firefox: http://jsperf.com/function-context-benchmark-3

0

Como se ha señalado en los comentarios a @nickf respuesta: La respuesta a

es la creación de una función una vez más rápido que la creación de un millón de veces

es simplemente sí. Pero como muestra su perfil JS, no es más lento en un factor de un millón, lo que demuestra que en realidad se vuelve más rápido con el tiempo.

La pregunta más interesante para mí es:

¿cómo una repetida crear + ejecutar comparar + para crear una vez repetida plazo.

Si una función realiza un cálculo complejo, es probable que el tiempo para crear el objeto de función sea insignificante. Pero ¿qué pasa con el exceso de create en los casos en que ejecuta es rápido? Por ejemplo:

// Variant 1: create once 
function adder(a, b) { 
    return a + b; 
} 
for (var i = 0; i < 100000; ++i) { 
    var x = adder(412, 123); 
} 

// Variant 2: repeated creation via function statement 
for (var i = 0; i < 100000; ++i) { 
    function adder(a, b) { 
    return a + b; 
    } 
    var x = adder(412, 123); 
} 

// Variant 3: repeated creation via function expression 
for (var i = 0; i < 100000; ++i) { 
    var x = (function(a, b) { return a + b; })(412, 123); 
} 

Este JS Perf muestra que la creación de la función de una sola vez es más rápido como se esperaba. Sin embargo, incluso con una operación muy rápida como un simple agregado, la sobrecarga de crear la función repetidamente es solo un pequeño porcentaje.

La diferencia probablemente sólo se vuelve significativo en los casos en crear el objeto función es complejo, mientras se mantiene un tiempo de ejecución insignificante, por ejemplo, si todo el cuerpo de la función se envuelve en un if (unlikelyCondition) { ... }.

Cuestiones relacionadas