Las variables de JavaScript declaran un bucle externo o interno?

213

En AS3, creo que debe inicializar todas las variables fuera de los bucles para aumentar el rendimiento. ¿Es este el caso con JavaScript también? ¿Cuál es mejor / más rápido / mejor práctica?

var value = 0;

for (var i = 0; i < 100; i++)
{
    value = somearray[i];
}

o

for (var i = 0 ; i < 100; i++)
{
    var value = somearray[i];
}
davivid
fuente
77
¡Fuera de! siempre afuera.
BGerrissen
37
Hmm, ¿no se empujan las declaraciones de variables al alcance de la función de todos modos en Javascript y AS3? Si estoy en lo correcto, entonces realmente no importa.
gastador
3
@Andy: ¿intentaste asignar antes de declarar en un cuerpo de función? Quizás tus ideas preconcebidas te están llevando por mal camino. Rendimiento WRT, con alcance push-up, si se interpreta el JS, entonces masticará ciclos adicionales dentro de un bloque de bucle. Si se compila (lo que la mayoría de los motores hacen hoy en día) no importará.
gastador
2
Gran pregunta! Gracias. Después de leer todas las respuestas, creo que si es solo un pequeño bucle o solo una variable temporal, las mantendré donde se necesiten y no afecte el rendimiento. Si se usa una var dentro de una función más de una vez, ¿por qué no hacer referencia a ella dentro de la función y, finalmente, las variables globales se pueden sentar fuera de fn ()
Dean Meehan
3
Me sorprende que nadie haya intentado medir el rendimiento. Creé un jsperf . Parece ser un poco más rápido cuando se declara dentro del bucle para Safari y Firefox, lo contrario para Chrome ...
Buzut

Respuestas:

281

No hay absolutamente ninguna diferencia en significado o rendimiento, en JavaScript o ActionScript.

vares una directiva para el analizador, y no un comando ejecutado en tiempo de ejecución. Si un identificador particular se ha declarado varuna vez o más en cualquier parte del cuerpo de una función (*), todo uso de ese identificador en el bloque se referirá a la variable local. No importa si valuese declara que está vardentro del bucle, fuera del bucle o ambos.

En consecuencia, debe escribir lo que le resulte más legible. No estoy de acuerdo con Crockford en que poner todos los vars en la parte superior de una función siempre es lo mejor. Para el caso en el que una variable se usa temporalmente en una sección de código, es mejor declarar varen esa sección, por lo que la sección es independiente y se puede copiar y pegar. De lo contrario, copie y pegue unas pocas líneas de código en una nueva función durante la refactorización, sin seleccionar y mover por separado lo asociado var, y obtendrá un global accidental.

En particular:

for (var i; i<100; i++)
    do something;

for (var i; i<100; i++)
    do something else;

Crockford le recomendará que elimine el segundo var(o elimine ambos varsy haga lo var i;anterior), y jslint se quejará de esto. Pero en mi opinión, es más varfácil mantener ambos s, manteniendo todo el código relacionado juntos, en lugar de tener un bit de código adicional y fácil de olvidar en la parte superior de la función.

Personalmente, tiendo a declarar como varla primera asignación de una variable en una sección de código independiente, si hay otro uso separado del mismo nombre de variable en alguna otra parte de la misma función. Para mí, tener que declarar vares una verruga JS indeseable (hubiera sido mejor tener las variables predeterminadas a locales); No veo como mi deber duplicar las limitaciones de [una revisión anterior de] ANSI C en JavaScript también.

(*: excepto en cuerpos de funciones anidadas)

bobince
fuente
44
Todavía no puedo decidir si estoy con Crockford o no en este caso. Declarar variables dentro de bloques se siente un poco como usar declaraciones de funciones condicionales (lo cual es malo) ... Sin embargo, también estoy de acuerdo con sus puntos :) #confused
Daniel Vassallo
20
+1 No estoy de acuerdo con el razonamiento de Crockford (citado en la respuesta de Daniel), como desarrolladores de JavaScript no deberíamos escribir código para que otros programadores de la "familia C" puedan entenderlo. A menudo utilizo var dentro de si los bloques y bucles, ya que hace más sentido para mí.
Andy E
44
-1 El OP pregunta si los vars en el cuerpo del bucle deben declararse antes de que comience el bucle. El valor de índice del bucle es claramente un caso especial (y está izado) y no ayuda en absoluto al OP.
mkoistinen
21
Los índices de bucle no son un caso especial, se manejan y se izan exactamente de la misma manera que una asignación normal.
bobince
31
+1 Crockford está equivocado sobre este (y otros, pero estoy divagando). Requerir que varsolo se use en la parte superior de una función es solo pedir la creación accidental de variables globales. Y tener una masa de variables no relacionadas, todas declaradas en un punto, carece de sentido semántico, especialmente cuando algunas de esas variables pueden terminar sin ser utilizadas.
MooGoo
64

En teoría, no debería hacer ninguna diferencia en JavaScript, ya que el lenguaje no tiene alcance de bloque, sino solo alcance de función.

No estoy seguro sobre el argumento del rendimiento, pero Douglas Crockford todavía recomienda que las vardeclaraciones sean las primeras en el cuerpo de la función. Citando las convenciones de código para el lenguaje de programación JavaScript :

JavaScript no tiene alcance de bloque, por lo que definir variables en bloques puede confundir a los programadores con experiencia en otros lenguajes de familia C. Defina todas las variables en la parte superior de la función.

Creo que tiene un punto, como puede ver en el siguiente ejemplo. Declarar las variables en la parte superior de la función no debe confundir a los lectores a pensar que la variable i se mantiene en el ámbito del forbloque de bucle:

function myFunction() {
  var i;    // the scope of the variables is very clear

  for (i = 0; i < 10; i++) {
    // ...
  }
}
Daniel Vassallo
fuente
8
+1 por decir verdades de OP sobre el alcance de JS. ¡Me pregunto si debo rechazar las respuestas que dicen lo contrario!
gastador
1
@Kieranmaine: No dije que no afecta el rendimiento. Acabo de hacer un argumento para ponerlos fuera de los lazos, irrelevantes para el rendimiento ... No tengo ninguna referencia para los argumentos de rendimiento, pero tampoco mencionaste ninguno en tu respuesta :)
Daniel Vassallo
1
@Kieranmaine: ¿tienes una fuente para eso?
Andy E
55
@Kieranmaine: AFAIK, incluso si declara variables dentro de un bucle, ecma- / javascriptlas aumentará en tiempo de ejecución. Eso se llama "elevación". Entonces no debería haber ninguna diferencia.
jAndy
1
¿Cómo letafectan los ES6 esta respuesta?
jbyrd
58

El ECMA-/Javascriptlenguaje hoistscualquier variable que se declara en cualquier lugar en la parte superior de una función Eso se debe a que esta lengua no tienen function scopey que no tienen block scopecomo muchos otros, C-como las lenguas.
Eso también se conoce como lexical scope.

Si declaras algo como

var foo = function(){
    for(var i = 0; i < 10; i++){
    }
};

Esto llega hoisteda:

var foo = function(){
    var i;
    for(i = 0; i < 10; i++){
    }
}

Por lo tanto, no hace ninguna diferencia en el rendimiento (pero corrígeme si estoy totalmente equivocado aquí).
Un argumento mucho mejor para no declarar una variable en otro lugar que no sea la parte superior de una función es la legibilidad . Declarar una variable dentro de a for-looppodría llevar a la suposición errónea de que solo se puede acceder a esta variable dentro del cuerpo del bucle, lo cual es totalmente incorrecto . De hecho, puede acceder a esa variable en cualquier lugar dentro del alcance actual.

jAndy
fuente
La misma respuesta básica que la aceptada pero, en mi opinión, más legible y casi tan informativa. Buen trabajo.
Anne Gunn
55
¿Cómo letafectan los ES6 esta respuesta?
jbyrd
13

El próximo año, todos los navegadores tendrán motores JS que precompilarán el código, por lo que la diferencia de rendimiento (que proviene de analizar el mismo bloque de código una y otra vez más la ejecución de la asignación) debería ser insignificante.

Además, nunca optimice el rendimiento a menos que sea necesario. Mantener variables cerca del lugar donde las necesita la primera vez mantiene limpio su código. En el lado negativo, las personas que están acostumbradas a los idiomas con ámbitos de bloque podrían estar confundidas.

Aaron Digulla
fuente
6

Otra consideración, ahora que tenemos lety consten ES2015, es que ahora puede abarcar variables específicamente para el bloque de bucle. Entonces, a menos que necesite la misma variable fuera del bucle (o si cada iteración depende de una operación realizada a esa variable en la iteración anterior), probablemente sea preferible hacer esto:

for (let i = 0; i < 100; i++) {
    let value = somearray[i];
    //do something with `value`
}
Matt Browne
fuente
4

Acabo de hacer una prueba simple en Chrome. Pruebe el violín en su navegador y vea los resultados.

  var count = 100000000;
    var a = 0;
    console.log(new Date());

    for (var i=0; i<count; i++) {
      a = a + 1
    }

    console.log(new Date());

    var j;
    for (j=0; j<count; j++) {
      a = a + 1;
    }

    console.log(new Date());

    var j;
    for (j=0; j<count; j++) {
        var x;
        x = x + 1;
    }

    console.log(new Date());

El resultado es que la última prueba dura ~ 8 segundos y las 2 anteriores son solo ~ 2 segundos. Muy repetible e independientemente del orden.

Entonces, esto me demuestra que uno siempre debe declarar los vars fuera del ciclo. El caso curioso para mí es el primero donde declaro ien la declaración for (). Esta parece ser tan rápida como la segunda prueba en la que pre-declaro el índice.

mkoistinen
fuente
14
@KP: los resultados solo se prueban si los prueba usted mismo o si un gran número de personas los verifica. @mkoistinen: construí una prueba más justa, jsfiddle.net/GM8nk . Después de ejecutar el script varias veces en Chrome 5, pude ver que no había un ganador consistente. Las tres variaciones funcionaron mejor que las otras después de algunas actualizaciones. -1 de mí, me temo. Tenga en cuenta que es posible que desee ejecutar este en otros navegadores. A IE y Fx no les gustaban 100 millones de iteraciones.
Andy E
1
@AndyE. Wow, entonces, basado en esta simple prueba, ¿IE apesta 100 veces más? =)
mkoistinen
2
Los resultados están por todas partes, no hay un claro ganador para todos los navegadores, aunque a veces hay diferencias de velocidad significativas. Extraño. Sin embargo, creo que el violín de Andy es una mejor prueba, ya que pone a cada candidato en su propia función ... ciertamente, si el script original se ejecuta fuera de una función, en realidad no debería probar nada, ya varque declara como una variable global que ser global de todos modos.
bobince
44
Más de un año después del hecho, pero SHAPOW
sdleihssirhc
2
Esto no es mío, pero pensé que algunos de ustedes estarían interesados: jsperf.com/var-in-for-loop
m1.
1

JavaScript es un lenguaje escrito en la parte inferior por C o C ++, no estoy muy seguro de cuál es. Y uno de sus propósitos es salvar el placer de manejar la memoria interna. Incluso en C o C ++, no tendrá que preocuparse por si consumirá muchos recursos cuando las variables se declaren dentro de un bucle. ¿Por qué deberías preocuparte en JavaScript?

Yan Yang
fuente
1
C o C ++ pueden estar en la parte inferior de JavaScript. Pero no debemos olvidar que el navegador convierte JavaScript al lenguaje subyacente (C, C ++). Por lo tanto, el rendimiento depende del navegador
Kira
3
@Kira En realidad no se convierte a C / C ++. Javascript se compila en un conjunto de instrucciones que ejecuta el tiempo de ejecución de JS (es decir, una máquina virtual que se ejecuta en el navegador). El mismo principio se aplica a otros lenguajes dinámicos como Python y Ruby.
Anthony E
@AnthonyE, gracias por la información. No estaba seguro acerca de la conversión de JS a C o C ++. Así que solía estar en mi comentario
Kira
0

Bueno, eso depende de lo que intente lograr ... si se valuesupone que es solo una variable temporal dentro del bloque de bucle, entonces es mucho más claro usar la segunda forma. También es más lógico y detallado.

Crozin
fuente
He descubierto que empujar la declaración de todas las variables a la parte superior, incluidas las variables temporales, en realidad puede generar confusión, ya que se vuelve 'ruidoso'.
Daniel Sokolowski
0

No hace diferencia si declaras variables dentro o fuera del ciclo for. A continuación se muestra el código de muestra para probar.

function a() {
   console.log('Function a() starts');
   console.log(new Date());
    var j;
    for (j=0; j<100000000; j++) {
        var x;
        x = x + 1;
    }
    console.log(new Date());
    console.log('Function a() Ends');
}
a()
function b() {
console.log('Function B() starts');
   console.log(new Date());
    var a;
    var j;
    for (j=0; j<100000000; j++) {
      a = a + 1;
    }
    console.log(new Date());
    console.log('Function B() Ends');
}
b()

Los resultados mostraron en mi caso

Function a() starts
VM121:3 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:9 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:10 Function a() Ends
VM121:14 Function B() starts
VM121:15 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:21 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:22 Function B() Ends

Gracias - MyFavs.in

myfavs.in
fuente
en ambos casos declaras j fuera del ciclo! X_x
john ktejik
Lo probé en Chromium 81 con en letlugar de vary a()tiende a ser un poco más lento (como 120 vs 115 ms = ~ 6% = IMO insignificante)
mikiqex
-1

La pregunta aquí es básicamente declarar una var dentro de un bucle. Solo piensa en lo que sucede si haces esto:

var a = 30;
var a = 50;
var a = 60;

¿Crees que esto es correcto? No ... porque no quieres declarar una variable tantas veces. Cuando declaras una variable dentro de un ciclo, ¿no está declarando tantas veces que el ciclo se ejecuta? Obviamente te abofeteará cuando estés en modo 'uso estricto'. La gente no estuvo de acuerdo con Crockford sin pensar en la pregunta original.

Por lo tanto, siempre es bueno declarar variables en la parte superior: 1. Para facilitar la lectura, 2. Hacer buenos hábitos.

Vivek Pohre
fuente
1
"Cuando declaras una variable dentro de un ciclo, ¿no está declarando tantas veces que el ciclo se ejecuta?" <- No, eso no está bien. La declaración de variable se iza, por lo que todo lo que queda es la asignación.
Matthias
-2

Con respecto al rendimiento después de ejecutar la prueba en Chrome, Firefox y jsperf en un sistema operativo Linux, parece haber una diferencia de rendimiento entre la declaración de variables en un bucle y fuera de un bucle. Es una pequeña diferencia, pero esto también se ve agravado por la cantidad de iteraciones y la cantidad de declaraciones variables.

Por lo tanto, para un mejor rendimiento, tendría que sugerir declarar variables fuera del ciclo. O mejor aún, declara tus variables en línea. Ver ejemplo.

// inline
for (var ai = 0, al = 100000000, av; ai < al; ai++) {
    av = av + 1;
}

// outside
var bv;
var bl = 100000000;
for (var bi = 0; bi < bl; bi++) {
    bv = bv + 1;
}

Observe cómo la variable 'al' y 'av' están en la línea de declaración de bucle for. Esta declaración en línea me ha proporcionado un rendimiento consistentemente mejor. Incluso sobre la declaración de variables fuera del ciclo. Nuevamente, la diferencia de rendimiento es realmente pequeña.

https://jsperf.com/outside-inline-for-loop-ase/1

AlexanderElias
fuente
Para mí su prueba dio en el bucle. Y sin embargo, no fue así, la diferencia es demasiado pequeña para concluir, y la respuesta aceptada explica claramente que no hay diferencia
Ulysse BN el
A medida que se izan declaraciones variables, realmente no hay diferencia.
Trincot