it-swarm-es.com

¿El uso de funciones anónimas afecta el rendimiento?

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

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;
}

El primero es más ordenado ya que no satura tu código con funciones raramente utilizadas, pero ¿importa que vuelvas a declarar esa función varias veces?

82
nickf

El problema de rendimiento aquí es el costo de crear un nuevo objeto de función en cada iteración del ciclo y no el hecho de que use una función anónima:

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

Está creando miles de objetos de función distintos a pesar de que tienen el mismo cuerpo de código y no están vinculados al ámbito léxico ( cierre ). Lo siguiente parece más rápido, por otro lado, porque simplemente asigna la misma referencia de función a los elementos de la matriz en todo el ciclo:

function myEventHandler() {
    // do something
}

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

Si tuviera que crear la función anónima antes de ingresar al bucle, solo asigne referencias a los elementos de la matriz mientras está dentro del bucle, encontrará que no hay ningún rendimiento o diferencia semántica en comparación con la versión de la función nombrada:

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

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

Por otro lado, puede parecer desde arriba que no hay diferencia entre:

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

y:

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

La primera es una declaración de función mientras que la segunda es una asignación variable a una función anónima. Aunque parezcan tener el mismo efecto, JavaScript los trata de manera ligeramente diferente. Para entender la diferencia, recomiendo leer, " ambigüedad de la declaración de función de JavaScript ".

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 del navegador moderno, visite el sitio JS Perf

81
Atif Aziz

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 que el motor JS no reconoce que es la misma función en Test2 y lo compila cada vez.

21
nickf

Como principio de diseño general, debe evitar implicar el mismo código varias veces. En su lugar, debe levantar 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 deduce de su pregunta) declara la función interna una vez y usa ese código una vez (y no tiene nada más idéntico en su programa), entonces una función anónima probablemente (eso es una suposición amigos) el compilador lo trata de la misma manera que una función con nombre normal.

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

2
Tom Leys

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

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

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

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

1
Pablo Estornut

No esperaría mucha diferencia, pero si hay una, es probable que varíe según el motor de scripts 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.

1
Joe Skora

Los objetos anónimos son más rápidos que los objetos con nombre. 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, que introduce una pequeña pero no trivial cantidad de sobrecarga.

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

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

1
pcorcoran

@nickf

Sin embargo, esa es una prueba bastante fatua, estás comparando la ejecución y la compilación tiempo que obviamente va a costar el método 1 (compila N veces, dependiendo del motor JS) con el método 2 (compila una vez). No puedo imaginar a un desarrollador de JS que pase su libertad condicional escribiendo el código de esa manera.

Un enfoque mucho más realista es la asignación anónima, ya que de hecho lo está utilizando para su documento. El método onclick es más parecido al siguiente, que de hecho favorece ligeramente el método anon.

Usando un marco de prueba similar al tuyo:


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
annakata

@nickf

(Ojalá tuviera el representante para comentar, pero acabo de encontrar este sitio)

Mi punto es que aquí hay confusión entre las funciones con nombre/anónimas y el caso de uso de ejecutar + compilar en una iteración. Como ilustré, la diferencia entre anon + named es insignificante en sí misma; estoy diciendo 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 de bloque constante + creación de objetos de este caso de uso es uno) y si no estás seguro, ¡prueba!

0
annakata

¡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 de código, considere usar funciones anónimas.

Aquí hay un artículo muy bueno sobre la optimización de JavaScript y funciones anónimas:

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

0
Christopher Tokar

una referencia casi siempre va a ser más lenta de lo que se refiere. Piénselo de esta manera: supongamos que desea imprimir el resultado de sumar 1 + 1. Lo que tiene más sentido:

alert(1 + 1);

o

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

Me doy cuenta de que es una forma realmente simplista de verlo, pero es ilustrativo, ¿verdad? Use una referencia solo si se va a usar 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 tiene más líneas. Esperemos que todo esto sea útil. (y la sintaxis de jquery no arrojó a nadie)

0
matt lohkamp

Como se señaló en los comentarios a @nickf respuesta: la respuesta a

Es crear una función una vez más rápido que crearla un millón de veces

es simplemente si. Pero como muestra su rendimiento JS, no es más lento por 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 se compara una ejecución repetida crear + ejecutar para crear una vez + ejecutar repetidamente .

Si una función realiza un cálculo complejo, lo más probable es que el tiempo para crear el objeto de función sea insignificante. Pero, ¿qué pasa con la sobrecarga de crear en casos donde ejecutar 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);
}

Esto JS Perf muestra que crear la función solo una vez es más rápido de lo esperado. 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 solo se vuelve significativa en los casos en que la creación del objeto de función es compleja, mientras se mantiene un tiempo de ejecución insignificante, por ejemplo, si todo el cuerpo de la función está envuelto en un if (unlikelyCondition) { ... }.

0
bluenote10

Lo que definitivamente hará que su bucle sea más rápido en una variedad de navegadores, especialmente IE navegadores, se repite de la siguiente manera:

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

Has puesto un arbitrario 1000 en la condición de bucle, pero obtienes mi deriva si querías revisar todos los elementos de la matriz.

0
Sarhanis