Zmienne JavaScript deklarują pętlę zewnętrzną czy wewnętrzną?

213

Uważam, że w AS3 należy zainicjować wszystkie zmienne poza pętlami w celu zwiększenia wydajności. Czy tak jest również w przypadku JavaScript? Który jest lepszy / szybszy / najlepsza praktyka?

var value = 0;

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

lub

for (var i = 0 ; i < 100; i++)
{
    var value = somearray[i];
}
dostojny
źródło
7
Na zewnątrz! zawsze na zewnątrz.
BGerrissen
37
Hmm, czy deklaracje zmiennych i tak nie są wypychane do zakresu funkcji w JavaScript i AS3? Jeśli mam rację, to naprawdę nie ma znaczenia.
spędzają
3
@Andy - próbowałeś przypisać, zanim zadeklarowałeś w treści funkcji? Być może twoje uprzedzenia prowadzą cię na manowce. Wydajność WRT, z zasięgiem push-up, jeśli JS zostanie zinterpretowany, wówczas będzie żuł dodatkowe cykle w bloku pętli. Jeśli zostanie skompilowany (co większość silników robi obecnie), nie będzie to miało znaczenia.
spędzać
2
Świetne pytanie! Dzięki. Po przeczytaniu wszystkich odpowiedzi uważam, że jeśli jest to tylko mała pętla lub tylko zmienna temp, zatrzymam je tam, gdzie są potrzebne i nie wpłynie to na wydajność. Jeśli var jest używany w funkcji więcej niż jeden raz, to dlaczego nie odwołać się do niego wewnątrz funkcji, a na końcu globals można usiąść poza fn ()
Dean Meehan
3
Dziwi mnie, że nikt nie próbował mierzyć wydajności. Stworzyłem jsperf . Wydaje się być trochę szybszy, gdy deklaruje się go w pętli dla Safari i Firefox, a odwrotnie dla Chrome…
Buzut

Odpowiedzi:

281

Nie ma absolutnie żadnej różnicy w znaczeniu lub wydajności w JavaScript lub ActionScript.

varjest dyrektywą dla parsera, a nie komendą wykonywaną w czasie wykonywania. Jeśli określony identyfikator został zadeklarowany varraz lub więcej w dowolnym miejscu w treści funkcji (*), wówczas wszelkie użycie tego identyfikatora w bloku będzie odnosić się do zmiennej lokalnej. Nie ma znaczenia, czy valuedeklaruje się, że varznajduje się w pętli, poza nią, czy w obu.

W związku z tym powinieneś napisać, co uznasz za najbardziej czytelne. Nie zgadzam się z Crockfordem, że umieszczenie wszystkich zmiennych na szczycie funkcji jest zawsze najlepszą rzeczą. W przypadku, gdy zmienna jest używana tymczasowo w sekcji kodu, lepiej zadeklarować varw tej sekcji, aby sekcja była samodzielna i można ją było wkleić. W przeciwnym razie skopiuj i wklej kilka wierszy kodu do nowej funkcji podczas refaktoryzacji, bez osobnego wybierania i przenoszenia powiązanych var, a otrzymasz przypadkowe globalne.

W szczególności:

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

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

Crockford zaleci usunięcie drugiego var(lub usuń oba vars i wykonaj var i;powyższe), a jslint będzie cię za to płakał. Ale IMO jest łatwiejsze w utrzymaniu, zachowując oba varpowiązane kody, zamiast mieć dodatkowy, łatwo zapominany kawałek kodu na górze funkcji.

Osobiście staram się zadeklarować jako varpierwsze przypisanie zmiennej w niezależnej sekcji kodu, niezależnie od tego, czy istnieje inne oddzielne użycie tej samej nazwy zmiennej w innej części tej samej funkcji. Dla mnie konieczność deklarowania varw ogóle jest niepożądaną brodawką JS (byłoby lepiej, gdyby zmienne były domyślnie lokalne); Nie uważam za swój obowiązek powielać ograniczeń [starej wersji] ANSI C również w JavaScript.

(*: inne niż w zagnieżdżonych obiektach funkcyjnych)

Bobin
źródło
4
Nadal nie mogę zdecydować, czy jestem z Crockfordem, czy nie. Deklarowanie zmiennych wewnątrz bloków przypomina trochę korzystanie z instrukcji funkcji warunkowych (co jest niegrzeczne) ... Jednak zgadzam się również z twoimi punktami :) #confused
Daniel Vassallo
20
+1 Nie zgadzam się z rozumowaniem Crockforda (cytowanym w odpowiedzi Daniela), ponieważ programiści JavaScript nie powinniśmy pisać kodu, aby inni programiści z „rodziny C” mogli go zrozumieć. Często używam var wnętrze jeśli bloki i pętle, ponieważ ma większy sens dla mnie.
Andy E,
4
-1 OP pyta, czy zmienne w ciele pętli powinny być deklarowane przed rozpoczęciem pętli. Wartość indeksu pętli jest wyraźnie specjalnym przypadkiem (i jest podnoszona) i wcale nie pomaga OP.
mkoistinen,
21
Indeksy pętli nie są specjalnym przypadkiem, są obsługiwane i podnoszone w dokładnie taki sam sposób, jak normalne przypisanie.
bobince
31
+1 Crockford myli się co do tego (i innych, ale dygresuję). Wymaganie, które varjest używane tylko u góry funkcji, wymaga jedynie przypadkowego utworzenia zmiennej globalnej. A posiadanie masy niepowiązanych zmiennych, wszystkie zadeklarowane w jednym miejscu, jest semantycznie nieistotne, szczególnie gdy niektóre z tych zmiennych mogą nigdy nie zostać użyte.
MooGoo,
64

Teoretycznie nie powinno to robić żadnej różnicy w JavaScript, ponieważ język nie ma zakresu bloków, a jedynie zakres funkcji.

Nie jestem pewien argumentu dotyczącego wydajności, ale Douglas Crockford nadal zaleca, aby varinstrukcje były pierwszymi instrukcjami w treści funkcji. Cytowanie z konwencji kodu dla języka programowania JavaScript :

JavaScript nie ma zakresu bloków, więc definiowanie zmiennych w blokach może dezorientować programistów mających doświadczenie z innymi językami rodziny C. Zdefiniuj wszystkie zmienne na górze funkcji.

Myślę, że ma rację, jak widać na poniższym przykładzie. Deklarowanie zmiennych na górze funkcji nie powinno dezorientować czytelników do myślenia, że ​​zmienna i jest przechowywana w zakresie forbloku pętli:

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

  for (i = 0; i < 10; i++) {
    // ...
  }
}
Daniel Vassallo
źródło
8
+1 za mówienie prawd OP o zakresie JS. Zastanawiam się, czy zanegować odpowiedzi, które mówią inaczej!
spędzają
1
@Kieranmaine: Nie powiedziałem, że to nie wpływa na wydajność. Właśnie argumentowałem za umieszczeniem ich poza pętlami, bez znaczenia dla wydajności ... Nie mam żadnych odniesień do argumentów dotyczących wydajności, ale nie przytoczyłeś też żadnej w swojej odpowiedzi :)
Daniel Vassallo
1
@Kieranmaine: czy masz na to źródło?
Andy E,
5
@Kieranmaine: AFAIK, nawet jeśli zadeklarujesz zmienne wewnątrz pętli, ecma- / javascriptpodbije je w czasie wykonywania. Nazywa się to „podnoszeniem”. Więc nie powinno być żadnej różnicy.
jAndy,
1
Jak ES6 letwpływa na tę odpowiedź?
jbyrd
58

ECMA-/JavascriptJęzyk hoistskażda zmienna, która jest zadeklarowana w dowolnym miejscu na szczycie funkcji. To dlatego, że język ten nie posiada function scopeani nie mają block scopejak wiele innych języków jak C.
To jest również znane jako lexical scope.

Jeśli zadeklarujesz coś takiego

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

To prowadzi hoisteddo:

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

Więc nie ma to żadnego wpływu na wydajność (Ale popraw mnie, jeśli się tu całkowicie mylę).
O wiele lepszym argumentem za nie deklarowaniem zmiennej gdzieś indziej niż na górze funkcji jest czytelność . Zadeklarowanie zmiennej w obrębie for-loopmoże prowadzić do błędnego założenia, że ​​dostęp do tej zmiennej można uzyskać tylko w ciele pętli, co jest całkowicie błędne . W rzeczywistości można uzyskać dostęp do tej zmiennej w dowolnym miejscu w bieżącym zakresie.

jAndy
źródło
Ta sama podstawowa odpowiedź, co zaakceptowana, ale IMO jest bardziej czytelna i prawie tak samo pouczająca. Dobra robota.
Anne Gunn,
5
Jak ES6 letwpływa na tę odpowiedź?
jbyrd
13

W przyszłym roku wszystkie przeglądarki będą miały silniki JS, które wstępnie kompilują kod, więc różnica w wydajności (wynikająca z powtarzania tego samego bloku kodu i wykonywania zadania) powinna być znikoma.

Nigdy też nie optymalizuj wydajności, chyba że musisz. Utrzymywanie zmiennych blisko miejsca, w którym są potrzebne, za pierwszym razem utrzymuje kod w czystości. Z drugiej strony ludzie przyzwyczajeni do języków o zakresie blokowym mogą być zdezorientowani.

Aaron Digulla
źródło
6

Kolejną kwestią, teraz, którą mamy leti constw ES2015, jest to, że możesz teraz zakresować zmienne specjalnie do bloku pętli. Tak więc, chyba że będziesz potrzebować tej samej zmiennej poza pętlą (lub jeśli każda iteracja zależy od operacji wykonanej na tej zmiennej w poprzedniej iteracji), prawdopodobnie najlepiej jest to zrobić:

for (let i = 0; i < 100; i++) {
    let value = somearray[i];
    //do something with `value`
}
Matt Browne
źródło
4

Właśnie zrobiłem prosty test w Chrome. Wypróbuj skrzypce w przeglądarce i zobacz wyniki

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

Rezultat jest taki, że ostatni test zajmuje ~ 8 sekund, a poprzednie 2 to tylko ~ 2 sekundy. Bardzo powtarzalnie i niezależnie od zamówienia.

Udowadnia mi to, że należy zawsze deklarować vars poza pętlą. Ciekawą sprawą jest dla mnie pierwsza, w której deklaruję iw instrukcji for (). Ten wydaje się być równie szybki jak drugi test, w którym wstępnie deklaruję indeks.

mkoistinen
źródło
14
@KP: wyniki są sprawdzane tylko wtedy, gdy je przetestujesz samodzielnie lub jeśli zweryfikuje je duża liczba osób. @mkoistinen: Zbudowałem bardziej sprawiedliwy test, jsfiddle.net/GM8nk . Po kilkukrotnym uruchomieniu skryptu w Chrome 5 zauważyłem, że nie ma spójnego zwycięzcy. Wszystkie trzy warianty działały lepiej niż pozostałe po kilku odświeżeniach. -1 ode mnie, obawiam się. Uwaga: możesz chcieć uruchomić ten w innych przeglądarkach. IE i Fx nie lubiły 100 milionów iteracji.
Andy E,
1
@AndyE. Wow, więc w oparciu o ten prosty test IE wysysa 100X więcej? =)
mkoistinen,
2
Wyniki są dla mnie wszędzie, nie ma wyraźnego zwycięzcy w różnych przeglądarkach, chociaż czasami występują znaczne różnice prędkości. Dziwne. Myślę, że skrzypce Andy'ego są jednak lepszym testem, stawiając każdego kandydata we własnej funkcji ... z pewnością jeśli oryginalny skrypt jest uruchamiany poza funkcją, nie powinien tak naprawdę testować niczego, ponieważ vardeklaruje jako zmienną globalną i tak być globalnym.
bobince
4
Ponad rok po fakcie, ale SHAPOW
sdleihssirhc,
2
To nie jest moje, ale pomyślałem, że niektórzy z was byliby zainteresowani: jsperf.com/var-in-for-loop
m1.
1

JavaScript to język napisany u dołu przez C lub C ++, nie jestem pewien, który to jest. A jednym z jego celów jest oszczędzanie poczucia obsługi pamięci wewnętrznej. Nawet w C lub C ++ nie będziesz musiał się martwić, czy zużyje dużo zasobów, gdy zmienne są deklarowane w pętli. Dlaczego warto się tym martwić w JavaScript?

Yan Yang
źródło
1
C lub C ++ może znajdować się na dole JavaScript. Ale nie powinniśmy zapominać, że przeglądarka konwertuje JavaScript na język podstawowy (C, C ++). Wydajność zależy więc od przeglądarki
Kira
3
@Kira W rzeczywistości nie jest konwertowany do C / C ++. JavaScript zostaje skompilowany w zestaw instrukcji wykonywanych przez środowisko wykonawcze JS (tj. Maszynę wirtualną uruchomioną w przeglądarce). Ta sama zasada dotyczy innych dynamicznych języków, takich jak Python i Ruby.
Anthony E
@AnthonyE, Dzięki za informację. Nie byłem pewien konwersji JS do C lub C ++. Więc użyłem może być w moim komentarzu
Kira
0

Zależy to od tego, co próbujesz osiągnąć ... jeśli valuezałożysz , że jest to tylko zmienna tymczasowa w bloku pętli, to o wiele łatwiej jest użyć drugiej postaci. Jest to również bardziej logiczne i pełne słów.

Crozin
źródło
Przekonałem się, że zepchnięcie wszystkich deklaracji zmiennych na szczyt - w tym zmiennych tymczasowych - może faktycznie prowadzić do zamieszania, ponieważ staje się „głośne”.
Daniel Sokołowski
0

Nie ma znaczenia, jeśli deklarujesz zmienne wewnątrz lub na zewnątrz pętli for. Poniżej znajduje się przykładowy kod do przetestowania.

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

Wyniki pokazały w moim przypadku

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

Dziękuję - MyFavs.in

myfavs.in
źródło
w obu przypadkach deklarujesz j poza pętlą! X_x
John ktejik
Wypróbowałem to w Chromium 81 letzamiast vari a()wydaje się być nieco wolniejsze (jak 120 vs 115 ms = ~ 6% = nieznaczne IMO)
mikiqex
-1

Pytanie tutaj polega na zadeklarowaniu zmiennej var w pętli. Pomyśl tylko, co się stanie, jeśli to zrobisz:

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

Myślisz, że to prawda? Nie ... ponieważ nie chcesz deklarować zmiennej tyle razy. Kiedy deklarujesz zmienną w pętli, czy nie deklaruje ona tyle razy, ile razy pętla działa? Oczywiście uderzy cię, gdy będziesz w trybie „użyj ścisłego”. Ludzie nie zgadzali się z Crockfordem, nie zastanawiając się nad pierwotnym pytaniem.

Dlatego zawsze dobrze jest deklarować zmienne na górze - 1. Dla czytelności, 2. Nawiązywanie dobrych nawyków.

Vivek Pohre
źródło
1
„Kiedy deklarujesz zmienną w pętli, czy nie deklaruje ona tyle razy, ile pętli się uruchamia?” <- Nie, to nie w porządku. Deklaracja zmiennej jest podnoszona, więc pozostaje Ci tylko przypisanie.
Matthias,
-2

Jeśli chodzi o wydajność po uruchomieniu testu w Chrome, Firefox i jsperf w systemie Linux, wydaje się, że istnieje różnica w wydajności między deklaracją zmiennych w pętli i poza nią. Jest to niewielka różnica, ale pogarsza ją także liczba iteracji i liczba deklaracji zmiennych.

Dlatego w celu uzyskania najlepszej wydajności musiałbym zasugerować deklarowanie zmiennych poza pętlą. Lub jeszcze lepiej zadeklaruj swoje zmienne w linii. Patrz przykład.

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

Zwróć uwagę, jak zmienne „al” i „av” znajdują się w wierszu deklaracji pętli for. Ta wewnętrzna deklaracja zapewniła mi konsekwentnie lepszą wydajność. Nawet ponad deklaracją zmiennych poza pętlą. Znowu różnica w wydajności jest naprawdę niewielka.

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

AlexanderElias
źródło
Dla mnie twój test dał się w pętli. A jednak tak nie było, różnica jest zbyt mała, aby stwierdzić, a przyjęta odpowiedź jasno wyjaśnia, że nie ma różnicy
Ulysse BN
Ponieważ deklaracje zmiennych są podnoszone, tak naprawdę nie ma różnicy.
trincot