JavaScript dla… w porównaniu z

461

Czy uważasz, że istnieje duża różnica w ... pętlach i pętlach? Jakiego rodzaju „dla” wolisz używać i dlaczego?

Powiedzmy, że mamy tablicę tablic asocjacyjnych:

var myArray = [{'key': 'value'}, {'key': 'value1'}];

Możemy więc iterować:

for (var i = 0; i < myArray.length; i++)

I:

for (var i in myArray)

Nie widzę dużej różnicy. Czy są jakieś problemy z wydajnością?

andrii
źródło
13
Zauważ, że my również, jako JS 1.6 , posiadają: myArray.forEach(callback[, thisarg]).
Benji XVI
14
@Benji array.forEach faktycznie znajduje się w ES5.
mikemaccana
2
w pętli for-in potrzebujesz warunku, który wygląda następująco:if(myArray.hasOwnProperty(i)){true}
Eric Hodonsky
6
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); jest OK, aby używać praktycznie wszędzie oprócz IE8 i jest to zdecydowanie najbardziej elegancka składnia
Jon z
5
Istnieje również for...ofoświadczenie w ECMAScript 6 , na przykład:for (let i of myArray) console.log(i);
Vitalii Fedorenko

Odpowiedzi:

548

Wybór powinien opierać się na tym, który idiom jest najlepiej rozumiany.

Tablica jest iterowana przy użyciu:

for (var i = 0; i < a.length; i++)
   //do stuff with a[i]

Obiekt używany jako tablica asocjacyjna jest iterowany przy użyciu:

for (var key in o)
  //do stuff with o[key]

Jeśli nie masz powodów do zniszczenia ziemi, trzymaj się ustalonego wzorca użytkowania.

AnthonyWJones
źródło
38
Należy wspomnieć, że jest to dobra praktyka do użycia z ... w przypadku filtrowania instrukcji if. Istnieje przydatna metoda obiektu „obj.hasOwnProperty (członek)”, która sprawdza, czy element zwrócony przez iterator jest rzeczywiście członkiem obiektu. Zobacz: javascript.crockford.com/code.html
Damir Zekić
57
Jak skomentowano w innej odpowiedzi, „for ... in” nie działa poprawnie dla tablic, ponieważ będzie iterować wszystkie właściwości i metody tablic. Dlatego powinieneś używać „for ... in” tylko do iteracji właściwości obiektu. W przeciwnym razie trzymaj się słowa „for (i = 0; i <coś; i ++)”
Denilson Sá Maia
Ze względu na wydajność IMO lepiej jest oszacować długość tablicy przed for, a nie oceniać a. Długość za każdym razem w pętli.
UpTheCreek
9
@UpTheCreek: To zdecydowanie prawda, gdy tablica jest w rzeczywistości czymś zwracanym przez HTMLDOM, ale zastanawiam się, jak duża musiałaby być standardowa tablica javascript, zanim zobaczysz zauważalną różnicę? Osobiście utrzymałbym kod tak prosty, jak to możliwe, dopóki nie okaże się, że konieczne jest zrobienie czegoś innego.
AnthonyWJones
2
@Pichan Myślę, że masz na myśli i < l, nie i < aw swoim stanie pętli.
Max Nanasy
161

Douglas Crockford zaleca w JavaScript: The Good Parts (strona 24), aby uniknąć używania for ininstrukcji.

Jeśli użyjesz for indo zapętlenia nazw właściwości w obiekcie, wyniki nie zostaną uporządkowane. Gorzej: Możesz uzyskać nieoczekiwane wyniki; obejmuje elementy odziedziczone z łańcucha prototypów i nazwę metod.

Wszystko oprócz właściwości można odfiltrować .hasOwnProperty. Ten przykładowy kod robi to, co prawdopodobnie pierwotnie chciałeś:

for (var name in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, name)) {
        // DO STUFF
    }
}
Benno Richters
źródło
70
for ... in jest idealnie odpowiedni do zapętlania właściwości obiektu. Nie nadaje się do zapętlania elementów tablicy. Jeśli nie rozumiesz różnicy między tymi scenariuszami, to tak, powinieneś unikać dla ... w; w przeciwnym razie zwariuj.
Shog9,
8
Chcesz podkreślić fakt, że NIE JEST ZAMÓWIONY! Może to być duży problem i trudny do złapania błąd.
Jason
4
+1 dla „obejmuje elementy odziedziczone z łańcucha prototypów i nazwę metod”. Będziesz się dobrze bawić, jeśli na przykład ktoś użyje twojego kodu z załadowanym Prototypem (nawet jeśli twój kod go nie używa).
ijw
13
Proszę nie zapominać, aby zadeklarowaćname zmienną: for(var name in object)..., w przeciwnym razie, jeśli kod jest wewnątrz funkcji na przykład, namezmienna w końcu jest własnością obiektu globalnego (przypisanie do identyfikatora nierejestrowanej to robi), również w nowej ECMAScript 5 Tryb ścisły, ten kod rzuci a ReferenceError.
CMS
6
@Nosredna: Występuje problem dotyczący kolejności iteracji w Chrome, zgłoszony przez nikogo innego niż Johna Resiga, oznaczony jako WontFix. code.google.com/p/chromium/issues/detail?id=883 . Nawet przed Chrome kolejność iteracji nie była taka sama w różnych przeglądarkach, jeśli usuniesz, a następnie dodasz właściwość ponownie. Również IE 9 zachowuje się bardzo podobnie do chrome (podobno do poprawy prędkości). Więc ... Przestańcie rozpowszechniać niedokładne informacje, bardzo naiwnie będziecie polegać na nich.
Juan Mendes,
62

FYI - Użytkownicy jQuery


each(callback)Metoda jQuery for( ; ; )domyślnie używa pętli i będzie używana for( in ) tylko, jeśli długość wynosiundefined .

Dlatego powiedziałbym, że korzystanie z tej funkcji jest bezpieczne.

Przykład :

$(['a','b','c']).each(function() {
    alert(this);
});
//Outputs "a" then "b" then "c"

Minusem korzystania z tego jest to, że jeśli robisz logikę inną niż interfejs użytkownika, twoje funkcje będą mniej przenośne dla innych platform. Theeach()Funkcja jest prawdopodobnie najlepiej zarezerwowane do użytku z selektorów jQuery i for( ; ; )może być wskazane inaczej.


Jason
źródło
4
Zawsze istnieje documentcloud.github.com/underscore, który ma _.each i wiele innych przydatnych funkcji
w00t
1
oznacza to również, czy mam właściwość length w moim obiekcie $ .each się nie powiedzie? np. x = {a: „1”, b: „2”, długość: 3}.
Onur Topal
29

różnice w wydajności zależą od rodzaju używanej pętli i przeglądarki.

Na przykład:

for (var i = myArray.length-1; i >= 0; i--)

jest prawie dwa razy szybszy w niektórych przeglądarkach niż:

for (var i = 0; i < myArray.length; i++)

Jednakże, chyba że twoje tablice są OGROMNE lub nie zapętlasz ich ciągle, wszystkie są wystarczająco szybkie. Poważnie wątpię, że zapętlenie tablicy jest wąskim gardłem w twoim projekcie (lub w każdym innym projekcie w tym zakresie)

Gen
źródło
5
Czy przechowywanie „myArray.length” w zmiennej przed zapętleniem spowodowałoby zniknięcie różnicy w wydajności? Domyślam się, że tak.
Tomalak
3
Nie. „MyArray.length” to właściwość, a nie metoda obiektu - nie oblicza się jego wartości. Przechowywanie jego wartości w zmiennej nic nie da.
jason
3
Tak jest. Właściwości nie są zmiennymi; mają kod get / set.
ste
12
Zwykle używamfor(var i = myArray.length; i--;)
Kevin,
2
Kod dla przejrzystości i czytelności. Nie dlatego, że to, co dzieje się, działa nieco szybciej w niektórych przeglądarkach, gdy używasz absurdalnie ogromnych tablic w danym momencie. Optymalizacje mogą się zmieniać, ale czytelność kodu (lub jego brak) nie ulegnie zmianie. Napisz kod, który inni mogą łatwo śledzić i pozwól optymalizatorom nadrobić zaległości we właściwym czasie.
aroth
26

Zauważ, że natywna metoda Array.forEach jest obecnie szeroko obsługiwana .

Sam Dutton
źródło
2
Co to robi? Czy ma problemy wymienione w innych postach (zapętlanie właściwości zamiast elementów tablicy)? Ponadto, ponieważ IE8 go nie obsługuje, stwierdzenie, że jest on szeroko obsługiwany, jest nieco trudne.
Rauni Lillemets,
2
choć użytkownicy * nix nie znoszą tego, IE8 to przede wszystkim użytkownicy sub-Windows7. to ogromna część rynku przeglądarek.
sbartell
2
@Rauni - Rozumiem, ale w przypadku komputerów stacjonarnych udział przeglądarki IE jest mniejszy niż 40%, zgodnie z en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table oraz według markethare.hitslink.com/... i innych stron, co najmniej 8% przeglądarek to IE 9. Innymi słowy, Array.forEach jest obsługiwany przez około 70% przeglądarek na komputery stacjonarne, więc nie sądzę, aby „szeroko obsługiwane” było nierozsądne. Nie sprawdziłem, ale obsługa mobilna (w przeglądarkach WebKit i Opera) może być jeszcze wyższa. Oczywiście istnieją znaczne różnice geograficzne.
Sam Dutton,
1
Dziękuję za aktualizację. Zgadzam się, że można powiedzieć, że jest „szeroko wspierany”. Jedynym problemem jest to, że jeśli użytkownik korzysta z tej metody JS, nadal musi napisać metodę tworzenia kopii zapasowej dla sprawy, jeśli nie jest obsługiwana.
Rauni Lillemets 27.09.11
1
@Rauni - możesz użyć podkładek es5-shim i es6-shim, aby automatycznie zapewnić metody tworzenia kopii zapasowych. github.com/es-shims/es5-shim
Michiel van der Blonk
24

Zaktualizowana odpowiedź dla bieżącej wersji wszystkich głównych przeglądarek 2012 - Chrome, Firefox, IE9, Safari i Opera obsługują natywną tablicę ES5. Dla każdego.

O ile nie masz żadnego powodu, aby natywnie obsługiwać IE8 (pamiętając, że użytkownicy mogą zapewnić ES5-shim lub ramkę Chrome, co zapewni odpowiednie środowisko JS), czystsze jest użycie właściwej składni języka:

myArray.forEach(function(item, index) {
    console.log(item, index);
});

Pełna dokumentacja dla array.forEach () znajduje się w MDN.

mikemaccana
źródło
1
Powinieneś naprawdę udokumentować parametry wywołania zwrotnego: 1. wartość elementu, 2. indeks elementu, 3. przemierzana tablica
narysowany
Słyszę, co mówisz, ale w tym przypadku nadmierne uproszczenie przesłania pełny zakres możliwości. Posiadanie zarówno indeksu, jak i wartości oznacza, że ​​może on służyć jako zamiennik zarówno dla ... wewnątrz, jak i dla każdego ... wewnątrz - z premią, której nie musisz pamiętać, która iteruje nad kluczami lub wartościami.
zwrócił uwagę
1
@Cory: ES5 forEach można dość łatwo dodać do starszych przeglądarek ES3. Mniej kodu to lepszy kod.
mikemaccana
2
@nailer Czy można tego używać zamiennie w tablicach i obiektach?
hitautodestruct
1
@hitautodestruct To część prototypu Array, a nie Object. Ogólnie rzecz biorąc, w społeczności teraz obiekty niebędące macierzami są nadal iterowane z „for (klucz var w obiekcie) {}”.
mikemaccana
14

Obie nie są takie same, gdy tablica jest rzadka.

var array = [0, 1, 2, , , 5];

for (var k in array) {
  // Not guaranteed by the language spec to iterate in order.
  alert(k);  // Outputs 0, 1, 2, 5.
  // Behavior when loop body adds to the array is unclear.
}

for (var i = 0; i < array.length; ++i) {
  // Iterates in order.
  // i is a number, not a string.
  alert(i);  // Outputs 0, 1, 2, 3, 4, 5
  // Behavior when loop body modifies array is clearer.
}
Mike Samuel
źródło
14

Użycie forEach do pominięcia łańcucha prototypów

Wystarczy szybki dodatek do powyższej odpowiedzi @ nailer , użycie forEach z Object.keys oznacza, że ​​możesz uniknąć iteracji w łańcuchu prototypów bez konieczności używania hasOwnProperty.

var Base = function () {
    this.coming = "hey";
};

var Sub = function () {
    this.leaving = "bye";
};

Sub.prototype = new Base();
var tst = new Sub();

for (var i in tst) {
    console.log(tst.hasOwnProperty(i) + i + tst[i]);
}

Object.keys(tst).forEach(function (val) {
    console.log(val + tst[val]);
});
meloncholia
źródło
2
cholera, to podstępne. Aby to zrobić, warto było przeczytać 50 innych postów. obj = {"pink": "ducks", red: "gęsi"}; Object.keys (obj) === [„różowy”, „czerwony”]
Orwellophile,
14

Druga opinia, że ​​powinieneś wybrać metodę iteracji zgodnie z twoimi potrzebami. Proponuję ci rzeczywiście nie do historii pętli poprzez rodem Arrayz for inkonstrukcji. Jest znacznie wolniejszy i , jak wskazał przed chwilą Chase Seibert, nie jest zgodny z frameworkiem Prototype.

Jest doskonały punkt odniesienia dla różnych stylów zapętlenia, na który absolutnie powinieneś się przyjrzeć, jeśli pracujesz z JavaScript . Nie rób wczesnych optymalizacji, ale powinieneś trzymać to gdzieś z tyłu głowy.

Użyłbym, for inaby uzyskać wszystkie właściwości obiektu, co jest szczególnie przydatne podczas debugowania twoich skryptów. Na przykład, lubię mieć tę linię pod ręką, kiedy eksploruję nieznany obiekt:

l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.log(l);

Zrzuca zawartość całego obiektu (wraz z ciałami metod) do mojego dziennika Firebug. Bardzo przydatny.

Damir Zekić
źródło
Link jest teraz zepsuty. Na pewno chciałbym zobaczyć test porównawczy, jeśli ktoś ma inny link.
Billbad
To już nie jest zepsute.
Olli,
Foreach pęka w prototypie? Jak to jest obecnie powszechnie obsługiwane, jest to coś, co powinien rozwiązać prototyp.
mvrak,
7

oto coś, co zrobiłem.

function foreach(o, f) {
 for(var i = 0; i < o.length; i++) { // simple for loop
  f(o[i], i); // execute a function and make the obj, objIndex available
 }
}

tak byś go użył to
zadziała na tablicach i obiektach (takich jak lista elementów HTML)

foreach(o, function(obj, i) { // for each obj in o
  alert(obj); // obj
  alert(i); // obj index
  /*
    say if you were dealing with an html element may be you have a collection of divs
  */
  if(typeof obj == 'object') { 
   obj.style.marginLeft = '20px';
  }
});

Właśnie to zrobiłem, więc jestem otwarty na sugestie :)


źródło
Świetne rzeczy - całkiem proste!
Chris
6

Używałbym różnych metod opartych na tym, jak chciałem odwoływać się do przedmiotów.

Użyj foreach, jeśli chcesz tylko bieżący element.

Użyj, jeśli potrzebujesz indeksatora do porównań względnych. (Tj. Jak to porównać do poprzedniego / następnego elementu?)

Nigdy nie zauważyłem różnicy w wydajności. Zaczęłam się martwić, dopóki nie będę mieć problemu z wydajnością.

Matt Lacey
źródło
Zobacz odpowiedź Bnos poniżej - ponieważ ... nie robi się tego, czego się tutaj spodziewasz, a jeśli go użyjesz, możesz dobrze się bawić. Dla przypomnienia, Prototype robi wszystko we właściwy sposób.
marcus.greasly
4

Za pomocą for (var i w myArray) możesz także zapętlać obiekty, ja będę zawierał nazwę klucza i możesz uzyskać dostęp do właściwości za pośrednictwem myArray [i] . Dodatkowo, wszelkie metody zostaną dodane do obiektu zostaną uwzględnione w pętli, zbyt, to znaczy, jeśli użyć dowolnego ramy zewnętrznej jak jQuery lub prototyp, lub jeśli dodać metod do prototypów obiektu bezpośrednio, w pewnym momencie i będzie wskazywać na te metody.

pilsetnieks
źródło
4

Uważaj!

Jeśli masz kilka znaczników skryptu i szukasz na przykład informacji w atrybutach znacznika, musisz użyć właściwości .length z pętlą for, ponieważ nie jest to prosta tablica, ale obiekt HTMLCollection.

https://developer.mozilla.org/en/DOM/HTMLCollection

Jeśli użyjesz instrukcji foreach dla (var i na twojej liście), zwróci ona właściwości i metody HTMLCollection w większości przeglądarek!

var scriptTags = document.getElementsByTagName("script");

for(var i = 0; i < scriptTags.length; i++)
alert(i); // Will print all your elements index (you can get src attribute value using scriptTags[i].attributes[0].value)

for(var i in scriptTags)
alert(i); // Will print "length", "item" and "namedItem" in addition to your elements!

Nawet jeśli getElementsByTagName powinien zwrócić NodeList, większość przeglądarek zwraca HTMLCollection: https://developer.mozilla.org/en/DOM/document.getElementsByTagName

baptx
źródło
3

Bo w pętlach na tablicach nie jest kompatybilny z Prototype. Jeśli uważasz, że w przyszłości będziesz musiał użyć tej biblioteki, warto trzymać się pętli.

http://www.prototypejs.org/api/array

Chase Seibert
źródło
Zapomnij o „może być konieczne użycie tej biblioteki”. Zamiast tego pomyśl: „Twoja JS może być dołączona do wszystkiego, co korzysta z tej biblioteki”, ponieważ problemy wciąż występują.
ijw
3

Widziałem problemy z „dla każdego” przy użyciu obiektów, prototypów i tablic

rozumiem, że dla każdego dotyczy właściwości obiektów, a NIE tablic

Benjamin Lee
źródło
3

Jeśli naprawdę chcesz przyspieszyć swój kod, co powiesz na to?

for( var i=0,j=null; j=array[i++]; foo(j) );

jest to rodzaj logiki while w instrukcji for i jest mniej zbędny. Również Firefox ma Array.forEach i Array.filter

fabjoa
źródło
2
Dlaczego to przyspieszy Twój kod? Nie rozumiem, dlaczego rejestrowanie takich oświadczeń przyspieszyłoby to.
Rup
3

Krótszy i najlepszy kod według jsperf to

keys  = Object.keys(obj);
for (var i = keys.length; i--;){
   value = obj[keys[i]];// or other action
}
bormat
źródło
1

Użyj pętli Array (). ForEach, aby skorzystać z równoległości

PazoozaTest Pazman
źródło
4
JavaScript w przeglądarce jest równoległy dla pętli zdarzeń, więc Array.prototype.forEachnie będzie wykonywać wielu wywołań zwrotnych równolegle.
Mike Samuel
1

dla (;;) jest dla tablic : [20,55,33]

for..in jest dla Objects : {x: 20, y: 55: z: 33}

angelito
źródło
0

Bądź ostrożny!!! Używam Chrome 22.0 w Mac OS i mam problem z każdą składnią.

Nie wiem, czy jest to problem z przeglądarką, problem z javascript lub jakiś błąd w kodzie, ale jest to BARDZO dziwne. Na zewnątrz obiektu działa idealnie.

var MyTest = {
    a:string = "a",
    b:string = "b"
};

myfunction = function(dicts) {
    for (var dict in dicts) {
        alert(dict);
        alert(typeof dict); // print 'string' (incorrect)
    }

    for (var i = 0; i < dicts.length; i++) {
        alert(dicts[i]);
        alert(typeof dicts[i]); // print 'object' (correct, it must be {abc: "xyz"})
    }
};

MyObj = function() {
    this.aaa = function() {
        myfunction([MyTest]);
    };
};
new MyObj().aaa(); // This does not work

myfunction([MyTest]); // This works
Paulo Check
źródło
0

Istnieje ważna różnica między nimi. Funkcja for-in iteruje właściwości obiektu, więc gdy przypadek jest tablicą, będzie iterował nie tylko swoje elementy, ale także funkcję „usuń”, którą posiada.

for (var i = 0; i < myArray.length; i++) { 
    console.log(i) 
}

//Output
0
1

for (var i in myArray) { 
    console.log(i) 
} 

// Output
0 
1 
remove

Możesz użyć for-in z if(myArray.hasOwnProperty(i)). Mimo to, podczas iteracji po tablicach zawsze wolę tego unikać i po prostu używam instrukcji for (;;).

achecopar
źródło
0

Chociaż oba są bardzo do siebie podobne, istnieje niewielka różnica:

var array = ["a", "b", "c"];
array["abc"] = 123;
console.log("Standard for loop:");
for (var index = 0; index < array.length; index++)
{
  console.log(" array[" + index + "] = " + array[index]); //Standard for loop
}

w tym przypadku wynikiem jest:

STANDARD DLA PĘTLI:

Tablica [0] = A

Tablica [1] = B

Tablica [2] = C

console.log("For-in loop:");
for (var key in array)
{
  console.log(" array[" + key + "] = " + array[key]); //For-in loop output
}

podczas gdy w tym przypadku wynikiem jest:

PĘTLA DOSTĘPNA:

Tablica [1] = B

Tablica [2] = C

Tablica [10] = D

Tablica [ABC] = 123

Yash Srivastava
źródło
0

Instrukcja for in pozwala na przeglądanie nazw wszystkich właściwości obiektu. Niestety, zapętla również wszystkie elementy odziedziczone przez łańcuch prototypów. Ma to zły efekt uboczny polegający na udostępnianiu funkcji metod, gdy zainteresowanie dotyczy elementów danych.

Fajaz
źródło