Le variabili JavaScript dichiarano loop esterno o interno?


213

In AS3 credo che dovresti inizializzare tutte le variabili al di fuori dei loop per migliorare le prestazioni. È il caso anche di JavaScript? Qual è la migliore / più veloce / la migliore pratica?

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

7
Al di fuori! sempre fuori.
BGerrissen,

37
Hmm, le dichiarazioni variabili non vengono spinte verso l'alto per funzionare comunque sia in Javascript che in AS3? Se ho ragione, allora non importa.
spender

3
@Andy - hai provato ad assegnare prima di dichiarare in un corpo di funzione? Forse i tuoi preconcetti ti stanno portando fuori strada. Prestazioni WRT, con ambito push-up, se viene interpretato il JS, masticherà cicli extra all'interno di un blocco loop. Se compilato (cosa che la maggior parte dei motori fa oggi) non importa.
spender

2
Ottima domanda! Grazie. Dopo aver letto tutte le risposte, credo che se è solo un piccolo ciclo o solo una variabile temporanea, le terrò dove sono necessarie e non influiscono sulle prestazioni. Se un var viene utilizzato all'interno di una funzione più di una volta, perché non fare riferimento ad esso all'interno della funzione e, infine, i globuli possono essere seduti fuori da fn ()
Dean Meehan,

3
Sono sorpreso che nessuno abbia provato a misurare le prestazioni. Ho creato un jsperf . Sembra essere un po 'più veloce quando dichiarato all'interno del loop per Safari e Firefox, il contrario per Chrome ...
Buzut

Risposte:


281

Non c'è assolutamente alcuna differenza nel significato o nelle prestazioni, in JavaScript o ActionScript.

varè una direttiva per il parser e non un comando eseguito in fase di esecuzione. Se un determinato identificatore è stato dichiarato varuna o più volte in un punto qualsiasi di un corpo di funzione (*), allora tutti gli usi di tale identificativo nel blocco si riferiranno alla variabile locale. Non fa alcuna differenza se valueviene dichiaratovar all'interno del loop, all'esterno del loop o in entrambi.

Di conseguenza dovresti scrivere quello che ritieni più leggibile. Non sono d'accordo con Crockford sul fatto che mettere tutti i vars in cima a una funzione sia sempre la cosa migliore. Nel caso in cui una variabile venga utilizzata temporaneamente in una sezione di codice, è meglio dichiararla varin quella sezione, in modo che la sezione rimanga sola e possa essere incollata. Altrimenti, copia e incolla alcune righe di codice in una nuova funzione durante il refactoring, senza selezionare e spostare separatamente il associato vare avrai un globale accidentale.

In particolare:

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

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

Crockford ti consiglierà di rimuovere il secondo var(o di rimuovere entrambi vari passaggi var i;precedenti), e jslint ti farà rabbia. Ma l'IMO è più mantenibile per mantenere entrambivar i messaggi, mantenendo insieme tutto il codice correlato, invece di avere un bit di codice aggiuntivo, facilmente dimenticabile nella parte superiore della funzione.

Personalmente tendo a dichiarare come varla prima assegnazione di una variabile in una sezione indipendente di codice, indipendentemente dal fatto che ci sia un altro uso separato dello stesso nome di variabile in qualche altra parte della stessa funzione. Per me, dover dichiarare varaffatto è una verruca JS indesiderabile (sarebbe stato meglio avere le variabili predefinite in locale); Non vedo come mio dovere duplicare i limiti di [una vecchia revisione di] ANSI C anche in JavaScript.

(*: diverso dai corpi funzione nidificati)


4
Non riesco ancora a decidere se sto con Crockford o no su questo. Dichiarare le variabili all'interno dei blocchi sembra un po 'come usare le istruzioni di funzione condizionale (che è cattivo) ... Tuttavia, sono d'accordo anche con i tuoi punti :) #confuso
Daniel Vassallo

20
+1 Non sono d'accordo con il ragionamento di Crockford (citato nella risposta di Daniel), in quanto sviluppatori JavaScript non dovremmo scrivere codice in modo che altri programmatori della "famiglia C" possano capirlo. Io uso spesso var dentro se blocchi e loop perché rende più senso per me.
Andy E

4
-1 L'OP chiede se devono essere dichiarate le varianze nel corpo del loop prima che inizi il loop. Il valore dell'indice del loop è chiaramente un caso speciale (ed è sollevato) e non aiuta affatto l'OP.
mkoistinen,

21
Gli indici di loop non sono un caso speciale, sono gestiti e sollevati esattamente come un normale incarico.
bobince,

31
+1 Crockford ha torto su questo (e altri, ma sto divagando). Il requisito che varviene utilizzato solo nella parte superiore di una funzione richiede solo la creazione accidentale di variabili globali. E avere una massa di variabili non correlate tutte dichiarate in un punto è semanticamente privo di significato, specialmente quando alcune di quelle variabili potrebbero non essere mai utilizzate.
MooGoo,

64

In teoria non dovrebbe fare alcuna differenza in JavaScript, poiché la lingua non ha un ambito di blocco, ma solo un ambito di funzione.

Non sono sicuro dell'argomento delle prestazioni, ma Douglas Crockford raccomanda ancora che le vardichiarazioni siano le prime dichiarazioni nel corpo della funzione. Citando da convenzioni di codice per il linguaggio di programmazione JavaScript :

JavaScript non ha ambito di blocco, quindi la definizione di variabili in blocchi può confondere i programmatori che hanno esperienza con altri linguaggi della famiglia C. Definire tutte le variabili nella parte superiore della funzione.

Penso che abbia un punto, come puoi vedere nell'esempio seguente. Dichiarare le variabili nella parte superiore della funzione non deve confondere i lettori nel pensare che la variabile i sia contenuta nell'ambito del forblocco loop:

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

  for (i = 0; i < 10; i++) {
    // ...
  }
}

8
+1 per dire verità OP sull'ambito JS. Mi chiedo se votare le risposte che dicono diversamente!
spender

1
@Kieranmaine: non ho detto che non influisce sulle prestazioni. Ho appena fatto una discussione per metterli fuori dai loop, irrilevanti per le prestazioni ... Non ho riferimenti per gli argomenti delle prestazioni, ma non ne hai citato nessuno nella tua risposta :)
Daniel Vassallo

1
@Kieranmaine: hai una fonte per questo?
Andy E

5
@Kieranmaine: AFAIK anche se dichiari le variabili all'interno di un ciclo, le ecma- / javascriptaumenterai durante l'esecuzione. Questo si chiama "sollevamento". Quindi non ci dovrebbero essere differenze.
jAndy,

1
In che modo ES6 letinfluenza questa risposta?
jbyrd,

58

La ECMA-/Javascriptlingua hoistsqualsiasi variabile dichiarata ovunque all'inizio di una funzione. Questo perché questo linguaggio non ha function scopee non non hanno block scopecome molti altri linguaggi C-like.
Questo è anche noto comelexical scope .

Se dichiari qualcosa del genere

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

Questo arriva hoisteda:

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

Quindi non fa alcuna differenza nelle prestazioni (ma correggimi se sbaglio qui).
Un argomento molto migliore per non dichiarare una variabile altrove che all'inizio di una funzione è la leggibilità . Dichiarare una variabile all'interno di a for-looppotrebbe portare all'ipotesi errata che questa variabile sia accessibile solo all'interno del corpo del loop, il che è totalmente sbagliato . Infatti puoi accedere a quella variabile ovunque all'interno dell'ambito attuale.


Stessa risposta di base a quella accettata ma, IMO, più leggibile e altrettanto istruttiva. Bel lavoro.
Anne Gunn,

5
In che modo ES6 letinfluenza questa risposta?
jbyrd,

13

Il prossimo anno, tutti i browser avranno motori JS che precompilano il codice, quindi la differenza di prestazioni (che deriva dall'analisi ripetuta dello stesso blocco di codice più volte e dall'esecuzione dell'assegnazione) dovrebbe diventare trascurabile.

Inoltre, non ottimizzare mai le prestazioni a meno che non sia necessario. Mantenere le variabili vicine al luogo in cui sono necessarie per la prima volta mantiene pulito il codice. Sul lato negativo, le persone che sono abituate alle lingue con ambiti di blocco potrebbero essere confuse.


6

Un'altra considerazione, ora che abbiamo lete constin ES2015, è che ora puoi estendere le variabili in modo specifico al blocco loop. Quindi, a meno che non sia necessaria la stessa variabile all'esterno del ciclo (o se ciascuna iterazione dipende da un'operazione eseguita su quella variabile nella precedente iterazione), è probabilmente preferibile farlo:

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

4

Ho appena fatto un semplice test in Chrome. Prova il violino nel tuo browser e vedi i risultati

  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());

Il risultato è che l'ultimo test dura ~ 8 secondi e i 2 precedenti sono solo ~ 2 secondi. Molto ripetibilmente e indipendentemente dall'ordine.

Quindi, questo mi dimostra che si dovrebbero sempre dichiarare le variazioni al di fuori del ciclo. Il caso curioso per me è il primo in cui dichiaro nell'istruzione ifor (). Questo sembra essere veloce quanto il secondo test in cui dichiaro in anticipo l'indice.


14
@KP: i risultati sono provati solo se li testerai tu stesso o se un gran numero di persone li verifica. @mkoistinen: ho costruito un test più equo, jsfiddle.net/GM8nk . Dopo aver eseguito lo script più volte in Chrome 5, ho visto che non c'era un vincitore coerente. Tutte e tre le variazioni hanno funzionato meglio delle altre dopo alcuni aggiornamenti. -1 da me, temo. Nota, potresti voler eseguire questo in altri browser. IE e Fx non hanno gradito 100 milioni di iterazioni.
Andy E

1
@AndyE. Wow, quindi in base a questo semplice test, IE succhia 100 volte di più? =)
mkoistinen,

2
I risultati sono ovunque per me, nessun chiaro vincitore tra browser anche se a volte ci sono differenze di velocità significative. Strano. Penso che il violino di Andy sia un test migliore, mettendo ogni candidato nella propria funzione ... sicuramente se lo script originale viene eseguito al di fuori di una funzione, non dovrebbe davvero testare nulla in quanto varsta dichiarando come globale una variabile che avrebbe essere globale comunque.
Bobince,

4
Oltre un anno dopo il fatto, ma SHAPOW
sdleihssirhc il

2
Questo non è mio, ma ho pensato che alcuni di voi sarebbero interessati: jsperf.com/var-in-for-loop
m1.

1

JavaScript è un linguaggio scritto in fondo da C o C ++, non sono molto sicuro di quale sia. E uno dei suoi scopi è quello di salvare il gusto di gestire la memoria interna. Anche in C o C ++, non dovrai preoccuparti se consumerà molte risorse quando le variabili vengono dichiarate all'interno di un ciclo. Perché dovresti preoccupartene in JavaScript?


1
C o C ++ potrebbero essere nella parte inferiore di JavaScript. Ma non dovremmo dimenticare che, il browser converte JavaScript nella lingua sottostante (C, C ++). Quindi le prestazioni dipendono dal browser
Kira

3
@Kira In realtà non viene convertito in C / C ++. Javascript viene compilato in un set di istruzioni che vengono eseguite dal runtime JS (ovvero una macchina virtuale in esecuzione nel browser). Lo stesso principio si applica ad altri linguaggi dinamici come Python e Ruby.
Anthony E,

@AnthonyE, grazie per le informazioni. Non ero sicuro della conversione di JS in C o C ++. Quindi ho usato potrebbe essere nel mio commento
Kira,

0

Bene, questo dipende da cosa stai cercando di ottenere ... se valuesupponi di essere solo una variabile temporanea all'interno del blocco del ciclo, è molto più chiaro usare la seconda forma. È anche più logico e dettagliato.


Ho scoperto che l'invio di tutte le dichiarazioni delle variabili verso l'alto - comprese le variabili temporanee - può effettivamente creare confusione in quanto diventa semplicemente "rumoroso".
Daniel Sokolowski

0

Non fa differenza se si dichiarano variabili all'interno o all'esterno di for loop. Di seguito è riportato il codice di esempio da testare.

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()

I risultati mostrati nel mio 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

Grazie - MyFavs.in


in entrambi i casi dichiari j fuori dal giro! X_x
john ktejik,

L'ho provato in Chromium 81 con letinvece di vare a()tende ad essere un po 'più lento (come 120 vs 115 ms = ~ 6% = IMO insignificante)
mikiqex

-1

La domanda qui è fondamentalmente dichiarare un var all'interno di un ciclo. Pensa solo a cosa succede se fai questo:

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

Pensi che sia giusto? No ... perché non vuoi dichiarare una variabile così tante volte. Quando dichiari una variabile all'interno di un ciclo, non sta dichiarando quante volte viene eseguito il ciclo? Ovviamente ti schiaffeggerà quando sarai in modalità "usa rigoroso". Le persone non sono d'accordo con Crockford senza pensare alla domanda originale.

Quindi è sempre bene dichiarare le variabili in alto - 1. Per leggibilità, 2. Fare buone abitudini.


1
"Quando dichiari una variabile all'interno di un ciclo, non sta dichiarando quante volte il ciclo viene eseguito?" <- No, non è esatto. La dichiarazione della variabile viene sollevata, quindi tutto ciò che ti rimane è l'assegnazione.
Matthias,

-2

Per quanto riguarda le prestazioni dopo l'esecuzione del test su Chrome, Firefox e jsperf su un sistema operativo Linux, sembra che ci sia una differenza di prestazioni tra la dichiarazione di variabili in un ciclo e fuori da un ciclo. È una piccola differenza, ma ciò è anche aggravato dalla quantità di iterazioni e dalla quantità di dichiarazioni variabili.

Pertanto, per le migliori prestazioni, dovrei suggerire di dichiarare le variabili al di fuori del ciclo. O meglio ancora dichiarare le variabili in linea. Vedi esempio.

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

Notare come la variabile 'al' e 'av' siano nella riga di dichiarazione del ciclo for. Questa dichiarazione in linea mi ha fornito prestazioni costantemente migliori. Anche sopra la dichiarazione di variabili al di fuori del ciclo. Anche in questo caso la differenza di prestazioni è davvero piccola.

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


Per me il tuo test ha dato dentro il ciclo. E comunque non lo è, la differenza è troppo piccola per concludere, e la risposta accettata spiega chiaramente che non c'è differenza
Ulysse BN

Dato che le dichiarazioni variabili vengono sollevate, non c'è davvero alcuna differenza.
trincot,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.