Szukałem tego, ale szukałem forEach i nie tylko for. jak powiedziano, w c # było trochę inaczej i to mnie zdezorientowało :)
Dante1986
6
ECMAScript & nbsp; 6 może zawierać konstrukcję „for… of”. Aby uzyskać więcej informacji, zobacz ... z (MDN). Możesz już wypróbować to w najnowszych wersjach Firefoksa.
Natomiast w Pythonie jest bardziej wydajny w użyciu funkcje niż do stosowania tradycyjnej pętli. (Zastanów się i < leni i++może to zrobić silnik, a nie tłumacz).
joeytwiddle 30.09.16
Odpowiedzi:
7018
TL; DR
Nie używaj, for-inchyba że używasz go z zabezpieczeniami lub przynajmniej wiesz, dlaczego może cię ugryźć.
Twoje najlepsze zakłady są zwykle
for-ofpętli (ES2015 + wyłącznie)
Array#forEach( spec| MDN) (lub jego krewni somei tak dalej) (tylko ES5 +),
prosta, staromodna forpętla,
lub for-inz zabezpieczeniami.
Ale jest o wiele więcej do odkrycia, czytaj dalej ...
JavaScript ma potężną semantykę do zapętlania tablic i obiektów podobnych do tablic. Podzieliłem odpowiedź na dwie części: Opcje dla prawdziwych tablic i opcje dla rzeczy, które są po prostu podobne do tablic , takich jak argumentsobiekt, inne iterowalne obiekty (ES2015 +), kolekcje DOM i tak dalej.
Ja szybko zauważyć, że można korzystać z opcji ES2015 teraz , nawet w silnikach ES5 przez transpiling ES2015 do ES5. Wyszukaj „ES2015 transpiling” / „ES6 transpiling”, aby uzyskać więcej ...
Ok, spójrzmy na nasze opcje:
Dla rzeczywistych tablic
Masz trzy opcje w ECMAScript 5 („ES5”), najbardziej obecnie obsługiwana wersja, i dwie kolejne dodane w ECMAScript 2015 („ES2015”, „ES6”):
Zastosowanie forEachi powiązane (ES5 +)
Użyj prostej forpętli
Używaj for-inpoprawnie
Użyj for-of(użyj pośrednio iteratora) (ES2015 +)
Użyj jawnie iteratora (ES2015 +)
Detale:
1. Wykorzystanie forEachi powiązane
W dowolnym nieco nowoczesnym środowisku (a więc nie IE8), w którym masz dostęp do Arrayfunkcji dodanych przez ES5 (bezpośrednio lub za pomocą wielopełniaczy), możesz użyć forEach( spec| MDN):
var a =["a","b","c"];
a.forEach(function(entry){
console.log(entry);});
forEachakceptuje funkcję wywołania zwrotnego i opcjonalnie wartość, która ma być używana thispodczas wywoływania tego wywołania zwrotnego (nieużywane powyżej). Wywołanie zwrotne jest wywoływane dla każdego wpisu w tablicy, w kolejności, pomijając nieistniejące wpisy w rzadkich tablicach. Chociaż użyłem tylko jednego argumentu powyżej, wywołanie zwrotne jest wywoływane z trzema: wartością każdego wpisu, indeksem tego wpisu i referencją do tablicy, nad którą się iteruje (na wypadek, gdyby twoja funkcja jeszcze tego nie miała) ).
O ile nie obsługujesz przestarzałych przeglądarek, takich jak IE8 (które NetApps pokazuje przy nieco ponad 4% udziale w rynku od tego pisania we wrześniu 2016 r.), Możesz z przyjemnością korzystać forEachz ogólnej strony internetowej bez cienia wątpliwości . Jeśli potrzebujesz obsługi przestarzałych przeglądarek, forEachłatwo jest wykonać shimming / polifilling (wyszukaj „es5 shim” dla kilku opcji).
forEach ma tę zaletę, że nie trzeba deklarować zmiennych indeksujących i wartościujących w zakresie zawierającym, ponieważ są one dostarczane jako argumenty do funkcji iteracji, a więc ładnie obejmują tylko tę iterację.
Jeśli martwisz się kosztami wykonania wywołania funkcji dla każdego wpisu tablicy, nie przejmuj się; szczegóły .
Dodatkowo forEachjest funkcja „pętli przez wszystkie”, ale w ES5 zdefiniowano kilka innych przydatnych funkcji „przeszukiwania tablicy i robienia rzeczy”, w tym:
every(przestaje zapętlać się po pierwszym powrocie wywołania zwrotnego falselub coś falsey)
some(przestaje zapętlać się po pierwszym powrocie wywołania zwrotnego truelub coś prawdziwego)
filter(tworzy nową tablicę zawierającą elementy, do których zwraca funkcja filtru truei pomija te, które zwraca false)
map (tworzy nową tablicę na podstawie wartości zwróconych przez wywołanie zwrotne)
reduce (buduje wartość poprzez wielokrotne wywoływanie wywołania zwrotnego, przekazywanie poprzednich wartości; zobacz specyfikację dla szczegółów; przydatne do sumowania zawartości tablicy i wielu innych rzeczy)
reduceRight(jak reduce, ale działa w kolejności malejącej niż rosnącej)
2. Użyj prostej forpętli
Czasami najlepsze są stare sposoby:
var index;var a =["a","b","c"];for(index =0; index < a.length;++index){
console.log(a[index]);}
Jeśli długość tablicy nie ulegnie zmianie podczas pętli, i to w wykonaniu wrażliwych kodu (mało prawdopodobne), nieco bardziej skomplikowana wersja chwytając długości do przodu może być malutki nieco szybciej:
var index, len;var a =["a","b","c"];for(index =0, len = a.length; index < len;++index){
console.log(a[index]);}
Ale w nowoczesnych silnikach JavaScript rzadko trzeba wyciągać ten ostatni kawałek soku.
W wersji ES2015 i nowszych możesz ustawić zmienne indeksu i wartości na lokalne dla forpętli:
let a =["a","b","c"];for(let index =0; index < a.length;++index){let value = a[index];
console.log(index, value);}//console.log(index); // would cause "ReferenceError: index is not defined"//console.log(value); // would cause "ReferenceError: value is not defined"
let a =["a","b","c"];for(let index =0; index < a.length;++index){let value = a[index];
console.log(index, value);}try{
console.log(index);}catch(e){
console.error(e);// "ReferenceError: index is not defined"}try{
console.log(value);}catch(e){
console.error(e);// "ReferenceError: value is not defined"}
A kiedy to zrobisz, nie tylko value, ale również indexjest odtworzony na każdej iteracji pętli, czyli zamknięcia utworzone w ciele pętli zachować odniesienie do index(i value) utworzona dla tej konkretnej iteracji:
let divs = document.querySelectorAll("div");for(let index =0; index < divs.length;++index){
divs[index].addEventListener('click', e =>{
console.log("Index is: "+ index);});}
let divs = document.querySelectorAll("div");for(let index =0; index < divs.length;++index){
divs[index].addEventListener('click', e =>{
console.log("Index is: "+ index);});}
Gdybyś miał pięć div, dostaniesz „Index is: 0”, jeśli klikniesz pierwszy i „Index is: 4”, jeśli klikniesz ostatni. To nie działa, jeśli używasz varzamiast let.
3. Używaj for-inpoprawnie
Dostaniesz ludzi mówiących ci, żebyś używał for-in, ale nie po to for-injest . for-inzapętla wyliczalne właściwości obiektu , a nie indeksy tablicy. Zamówienie nie jest gwarantowane , nawet w ES2015 (ES6). ES2015 + nie definiuje zamówienia do właściwości obiektów (przez [[OwnPropertyKeys]], [[Enumerate]]i rzeczy, które ich używają podoba Object.getOwnPropertyKeys), ale nie określają, że for-inpójdzie zlecenia; ES2020 jednak. (Szczegóły w tej drugiej odpowiedzi .)
Jedynymi rzeczywistymi przypadkami użycia dla for-intablicy są:
Używasz właściwości nieelementowych i chcesz uwzględnić je w pętli
Patrząc tylko na pierwszy przykład: możesz użyć for-indo odwiedzenia tych rzadkich elementów tablicy, jeśli zastosujesz odpowiednie zabezpieczenia:
// `a` is a sparse arrayvar key;var a =[];
a[0]="a";
a[10]="b";
a[10000]="c";for(key in a){if(a.hasOwnProperty(key)&&// These checks are/^0$|^[1-9]\d*$/.test(key)&&// explained
key <=4294967294// below){
console.log(a[key]);}}
Że przedmiot ma swój własny obiekt o tej samej nazwie (nie jeden to dziedziczy od swojego prototypu) oraz
Że kluczem są wszystkie cyfry dziesiętne (np. Normalna postać łańcucha, a nie notacja naukowa), i
Wartość klucza po wymuszeniu na liczbę wynosi <= 2 ^ 32 - 2 (czyli 4 294 967 294). Skąd ta liczba? Jest to część definicji indeksu tablicowego w specyfikacji . Inne liczby (liczby całkowite, liczby ujemne, liczby większe niż 2 ^ 32-2) nie są indeksami tablicowymi. Wynika to z tego, że 2 ^ 32 - 2 sprawia, że najwyższa wartość indeksu jest mniejsza niż 2 ^ 32 - 1 , czyli maksymalna wartość, jaką lengthmoże mieć tablica . (Np. Długość tablicy mieści się w 32-bitowej liczbie całkowitej bez znaku). (Proponuje RobGowi wskazanie w komentarzu na moim blogu, że mój poprzedni test nie był całkiem poprawny .)
Oczywiście nie zrobiłbyś tego w kodzie wbudowanym. Napisziłbyś funkcję narzędzia. Być może:
// Utility function for antiquated environments without `forEach`var hasOwn =Object.prototype.hasOwnProperty;var rexNum =/^0$|^[1-9]\d*$/;function sparseEach(array, callback, thisArg){var index;for(var key in array){
index =+key;if(hasOwn.call(a, key)&&
rexNum.test(key)&&
index <=4294967294){
callback.call(thisArg, array[key], index, array);}}}var a =[];
a[5]="five";
a[10]="ten";
a[100000]="one hundred thousand";
a.b ="bee";
sparseEach(a,function(value, index){
console.log("Value at "+ index +" is "+ value);});
Pod osłonami pobiera iterator z tablicy i przechodzi przez nią, uzyskując z niej wartości. Nie ma to problemu, który for-inma, ponieważ używa iteratora zdefiniowanego przez obiekt (tablicę), a tablice definiują, że ich iteratory iterują przez swoje wpisy (a nie ich właściwości). Inaczej niż for-inw ES5, kolejność odwiedzania wpisów to kolejność numeryczna ich indeksów.
5. Użyj jawnie iteratora (ES2015 +)
Czasami możesz chcieć jawnie użyć iteratora . Możesz to również zrobić, chociaż jest to znacznie bardziej niezręczne niż . To wygląda tak:for-of
const a =["a","b","c"];const it = a.values();let entry;while(!(entry = it.next()).done){
console.log(entry.value);}
Iterator jest obiektem pasującym do definicji Iterator w specyfikacji. Ta nextmetoda zwraca nowy obiekt wyniku za każdym razem, gdy go wywołujesz. Wynikowy obiekt ma właściwość, która donemówi nam, czy zostało wykonane, oraz właściwość valueo wartości dla tej iteracji. ( donejest opcjonalny, jeśli byłby false, valuejest opcjonalny, gdyby był undefined).
Znaczenie valueróżni się w zależności od iteratora; tablice obsługują (co najmniej) trzy funkcje zwracające iteratory:
values(): Tego użyłem powyżej. Zwraca iteracyjnej, gdzie każdy valuejest wpis tablicy w tej iteracji ( "a", "b"i "c"w tym przykładzie wcześniej).
keys(): Zwraca iterator, w którym każdy valuejest kluczem do tej iteracji (więc dla naszego apowyższego byłoby "0"to "1"wtedy "2").
entries(): Zwraca iterator, w którym każdy valuejest tablicą w formie [key, value]dla tej iteracji.
Dla obiektów podobnych do tablicy
Oprócz prawdziwych tablic istnieją również obiekty podobne do tablic, które mają lengthwłaściwość i właściwości o nazwach numerycznych: NodeListinstancje, argumentsobiekt itp. Jak przeglądamy ich zawartość?
Użyj dowolnej z powyższych opcji dla tablic
Przynajmniej niektóre, a być może większość lub nawet wszystkie powyższe podejścia tablicowe często stosuje się równie dobrze do obiektów podobnych do tablicy:
Zastosowanie forEachi powiązane (ES5 +)
Różne włączone funkcje Array.prototypesą „celowo ogólne” i zwykle można ich używać na obiektach podobnych do tablicy za pomocą Function#calllub Function#apply. (Zobacz zastrzeżenie dotyczące obiektów dostarczonych przez hosta na końcu tej odpowiedzi, ale jest to rzadki problem).
Załóżmy, że chcesz skorzystać forEachna Node„s childNodesnieruchomości. Zrobiłbyś to:
Array.prototype.forEach.call(node.childNodes,function(child){// Do something with `child`});
Jeśli zamierzasz to często robić, możesz pobrać kopię odwołania do funkcji do zmiennej w celu ponownego wykorzystania, np .:
// (This is all presumably in some scoping function)var forEach =Array.prototype.forEach;// Then later...
forEach.call(node.childNodes,function(child){// Do something with `child`});
Użyj prostej forpętli
Oczywiście prosta forpętla dotyczy obiektów podobnych do tablicy.
Używaj for-inpoprawnie
for-inz tymi samymi zabezpieczeniami, co w przypadku tablicy, powinien również działać z obiektami podobnymi do tablicy; może mieć zastosowanie zastrzeżenie dotyczące obiektów dostarczonych przez hosta w punkcie 1 powyżej.
Użyj for-of(użyj pośrednio iteratora) (ES2015 +)
for-ofużywa iteratora dostarczonego przez obiekt (jeśli istnieje). Obejmuje to obiekty dostarczone przez hosta. Na przykład specyfikacja dla NodeListod querySelectorAllzostała zaktualizowana w celu obsługi iteracji. Specyfikacja dla HTMLCollectionod getElementsByTagNamenie była.
Użyj jawnie iteratora (ES2015 +)
Zobacz # 4.
Utwórz prawdziwą tablicę
Innym razem możesz chcieć przekonwertować obiekt podobny do tablicy na prawdziwą tablicę. Jest to zaskakująco łatwe:
Użyj slicemetody tablic
Możemy użyć slicemetody tablic, która podobnie jak inne metody wspomniane powyżej, jest „celowo ogólna” i dlatego może być stosowana z obiektami podobnymi do tablic, takimi jak to:
var trueArray =Array.prototype.slice.call(arrayLikeObject);
Na przykład, jeśli chcemy przekonwertować NodeListtablicę na prawdziwą, moglibyśmy to zrobić:
var divs =Array.prototype.slice.call(document.querySelectorAll("div"));
Zobacz zastrzeżenie dotyczące obiektów udostępnianych przez hosta poniżej. W szczególności zauważ, że to się nie powiedzie w IE8 i wcześniejszych wersjach, co nie pozwala na używanie obiektów dostarczonych przez hosta w thisten sposób.
Użyj składni spread ( ...)
Możliwe jest także użycie składni rozproszonej ES2015 z silnikami JavaScript obsługującymi tę funkcję. Podobnie for-of, używa iteratora dostarczonego przez obiekt (patrz # 4 w poprzedniej sekcji):
var trueArray =[...iterableObject];
Na przykład, jeśli chcemy przekonwertować NodeListtablicę na prawdziwą, przy składni rozproszonej staje się to dość zwięzłe:
var divs =[...document.querySelectorAll("div")];
Posługiwać się Array.from
Array.from(specyfikacja) | (MDN) (ES2015 +, ale łatwo wypełniany) tworzy tablicę z obiektu podobnego do tablicy, opcjonalnie przepuszczając najpierw wpisy przez funkcję odwzorowania. Więc:
var divs =Array.from(document.querySelectorAll("div"));
Lub jeśli chcesz uzyskać tablicę nazw znaczników elementów z daną klasą, skorzystaj z funkcji mapowania:
// Arrow function (ES2015):var divs =Array.from(document.querySelectorAll(".some-class"), element => element.tagName);// Standard function (since `Array.from` can be shimmed):var divs =Array.from(document.querySelectorAll(".some-class"),function(element){return element.tagName;});
Zastrzeżenie dotyczące obiektów udostępnianych przez hosta
Jeśli używasz Array.prototypefunkcji z obiektami tablicowymi udostępnianymi przez hosta (listami DOM i innymi rzeczami udostępnianymi przez przeglądarkę, a nie z silnika JavaScript), musisz koniecznie przetestować w środowiskach docelowych, aby upewnić się, że obiekt dostarczony przez hosta działa poprawnie . Większość zachowuje się poprawnie (teraz), ale ważne jest, aby przetestować. Powodem jest to, że większość Array.prototypemetod, których prawdopodobnie chcesz użyć, opiera się na obiekcie dostarczonym przez hosta, dając uczciwą odpowiedź na [[HasProperty]]operację abstrakcyjną . W tym momencie przeglądarki wykonują bardzo dobrą robotę, ale specyfikacja 5.1 pozwoliła na to, że obiekt udostępniony przez hosta może nie być uczciwy. Znajduje się w §8.6.2 , kilka akapitów poniżej dużej tabeli blisko początku tej sekcji), gdzie jest napisane:
Obiekty hosta mogą implementować te metody wewnętrzne w jakikolwiek sposób, chyba że określono inaczej; Na przykład, jedną z możliwości jest, [[Get]]a [[Put]]dla danego obiektu gospodarza rzeczywiście pobierania i przechowywania wartości własności, ale [[HasProperty]]zawsze powoduje fałszywe .
(Nie może znaleźć równoważne słownictwa w ES2015 specyfikacji, ale to wiąże się jeszcze w przypadku). Ponownie, w tym piśmie wspólnego gospodarza pod warunkiem, tablicowej obiektów przeglądarkami [ NodeListprzypadkach, na przykład] wykonania uchwytu [[HasProperty]]poprawnie, ale ważne jest, aby przetestować).
Chciałbym również dodać, że .forEachnie można tego skutecznie złamać. Musisz rzucić wyjątek, aby wykonać przerwę.
Pijusn
82
@Pius: Jeśli chcesz przerwać pętlę, możesz użyć some. (Wolałbym również pozwolić na łamanie forEach, ale oni, hmm, nie pytali mnie; ;-))
TJ Crowder
6
@TJCrowder Prawda, chociaż wygląda bardziej jak obejście, ponieważ nie jest to jej główny cel.
Pijusn
8
@ user889030: Potrzebujesz ,po k=0, a nie ;. Pamiętaj, że programowanie to wiele rzeczy, z których jedną jest zwrócenie szczególnej uwagi na szczegóły ... :-)
TJ Crowder
5
@JimB: Omówiono to powyżej (i lengthnie jest to metoda). :-)
TJ Crowder
512
Uwaga : ta odpowiedź jest beznadziejnie nieaktualna. Aby uzyskać bardziej nowoczesne podejście, spójrz na metody dostępne w tablicy . Metodami zainteresowania mogą być:
dla każdego
mapa
filtr
zamek błyskawiczny
redukować
każdy
trochę
Standardowym sposobem iteracji tablicy w JavaScript jest forpętla waniliowa :
var length = arr.length,
element =null;for(var i =0; i < length; i++){
element = arr[i];// Do something with element}
Zauważ jednak, że takie podejście jest dobre tylko wtedy, gdy masz gęstą tablicę, a każdy indeks jest zajęty przez element. Jeśli tablica jest rzadka, to przy takim podejściu możesz napotkać problemy z wydajnością, ponieważ będziesz iterował wiele indeksów, które tak naprawdę nie istnieją w tablicy. W takim przypadku for .. inlepszym pomysłem może być pętla. Jednakże , należy użyć odpowiednich zabezpieczeń w celu zapewnienia, że tylko pożądanych właściwości tablicy (czyli elementy tablicy) są rozpatrzony, ponieważ for..in-loop zostaną również wymienione w starszych przeglądarkach, czy dodatkowe właściwości są definiowane jako enumerable.
W ECMAScript 5 na prototypie macierzy będzie metoda forEach, ale nie jest ona obsługiwana w starszych przeglądarkach. Aby móc konsekwentnie z niego korzystać, musisz mieć środowisko, które go obsługuje (na przykład Node.js dla JavaScript po stronie serwera), lub użyć „Polyfill”. Wielokrotne wypełnianie tej funkcji jest jednak trywialne, a ponieważ ułatwia odczytanie kodu, dobrym rozwiązaniem jest włączenie wielokrotnego wypełniania.
dlaczego for(instance in objArray) nie jest prawidłowe użycie? wydaje mi się to prostsze, ale słyszę, że mówisz o nim jako o niewłaściwym sposobie użycia?
Dante1986,
21
Możesz użyć buforowania długości w linii: dla (var i = 0, l = arr.length; i <l; i ++)
Robert Hoffmann
3
Czy przecinek na końcu pierwszego wiersza jest zamierzony, czy może literówka (może być średnikiem)?
Mohd Abdul Mujib
6
@ wardha-Web To celowe. Umożliwia nam deklarowanie wielu zmiennych za pomocą jednego słowa kluczowego var. Gdybyśmy użyli średnika, elementbylibyśmy zadeklarowani w zakresie globalnym (a raczej JSHint krzyczałby na nas, zanim dotarłby do produkcji).
Prawdopodobnie najczęściej używam tej odpowiedzi. To nie jest najlepsza odpowiedź na pytanie, ale w praktyce będzie najprostsza i najbardziej odpowiednia dla tych z nas, którzy używają jQuery. Sądzę jednak, że wszyscy powinniśmy nauczyć się także sposobu waniliowego. Nigdy nie boli poszerzanie twojego zrozumienia.
mopsyd
47
Po prostu: każdy z jQuery jest znacznie wolniejszy niż rozwiązania natywne. JQuery zaleca używanie natywnego JavaScript zamiast jQuery, gdy tylko jest to możliwe. jsperf.com/browser-diet-jquery-each-vs-for-loop
Kevin Boss
8
Powstrzymaj się od używania jQuery, gdy możesz używać waniliowego js
Noe
2
Trzymaj się standardowego JS, trzymaj biblioteki stron trzecich z dala od odpowiedzi, chyba że nie ma rozwiązania w języku ojczystym
scubasteve
116
Pętla do tyłu
Myślę, że odwrotność pętli for zasługuje na wzmiankę tutaj:
for(var i = array.length; i--;){// process array[i]}
Zalety:
Nie musisz deklarować lenzmiennej tymczasowej ani porównywać jej array.lengthprzy każdej iteracji, z których każda może być drobiazgową optymalizacją.
Usuwanie rodzeństwa z DOM w odwrotnej kolejności jest zwykle bardziej wydajne . (Przeglądarka musi wykonywać mniejsze przesunięcia elementów w swoich wewnętrznych tablicach.)
Jeśli zmodyfikujesz tablicę podczas zapętlania, w lub po indeksie i (na przykład usuniesz lub wstawisz element w array[i]), wówczas pętla do przodu pominie element, który przesunął się w lewo do pozycji i , lub ponownie przetworzy i- ty element, który był przesunął się w prawo. W tradycyjnej pętli for można aktualizować i, aby wskazywać następny element, który wymaga przetworzenia - 1, ale po prostu odwrócenie kierunku iteracji jest często prostszym i bardziej eleganckim rozwiązaniem .
Podobnie podczas modyfikowania lub usuwania zagnieżdżonych elementów DOM przetwarzanie w odwrotnej kolejności pozwala uniknąć błędów . Na przykład rozważ zmodyfikowanie innerHTML węzła nadrzędnego przed obsługą jego elementów podrzędnych. Do momentu osiągnięcia węzła potomnego zostanie on odłączony od DOM, który został zastąpiony przez nowo utworzone dziecko, gdy został napisany innerHTML rodzica.
Pisanie i czytanie jest krótsze niż w przypadku niektórych innych dostępnych opcji. Chociaż przegrywa forEach()z ES6 for ... of.
Niedogodności:
Przetwarza elementy w odwrotnej kolejności. Jeśli budujesz nową tablicę z wyników lub drukujesz rzeczy na ekranie, naturalnie dane wyjściowe zostaną odwrócone w stosunku do oryginalnej kolejności.
Wielokrotne wstawianie rodzeństwa do DOM jako pierwszego dziecka w celu zachowania ich kolejności jest mniej wydajne . (Przeglądarka musiałaby ciągle zmieniać ustawienia). Aby wydajnie i po kolei tworzyć węzły DOM, wystarczy zapętlić i przesłać w normalny sposób (a także użyć „fragmentu dokumentu”).
Pętla zwrotna jest myląca dla młodszych programistów. (Możesz uznać tę korzyść, w zależności od twojej perspektywy).
Czy powinienem go zawsze używać?
Niektórzy programiści domyślnie używają pętli zwrotnej dla pętli domyślnej , chyba że istnieje uzasadniony powód, aby pętli do przodu.
Chociaż wzrost wydajności jest zwykle niewielki, to rodzaj krzyków:
„Po prostu zrób to dla każdego elementu na liście, nie dbam o zamówienie!”
Jednak w praktyce nie jest to wiarygodne wskazanie zamiaru, ponieważ jest nie do odróżnienia od sytuacji, w których dbasz o porządek i naprawdę potrzebujesz zapętlić w odwrotnej kolejności. Tak więc w rzeczywistości potrzebny byłby inny konstrukt, aby dokładnie wyrazić zamiar „nie przejmuj się”, coś, co jest obecnie niedostępne w większości języków, w tym ECMAScript, ale które można nazwać na przykład forEachUnordered().
Jeśli kolejność nie ma znaczenia, a wydajność jest istotna (w najbardziej wewnętrznej pętli gry lub silnika animacji), może być dopuszczalne użycie odwrotnej pętli for jako wzorca. Pamiętaj tylko, że zobaczenie odwrotnej pętli for w istniejącym kodzie niekoniecznie oznacza, że kolejność nie ma znaczenia!
Lepiej było użyć forEach ()
Zasadniczo w przypadku kodu wyższego poziomu, w którym większe znaczenie mają przejrzystość i bezpieczeństwo , wcześniej zalecałem używanie Array::forEachjako domyślnego wzorca zapętlania (chociaż obecnie wolę używaćfor..of ). Powody, dla których wolisz forEachod pętli zwrotnej są:
Czytelniej jest czytać.
Oznacza to, że ja nie zostanie przesunięta w bloku (co zawsze jest możliwe ukrywanie niespodzianka w długifor i whilepętle).
Daje ci to swobodę zamykania.
Zmniejsza to wyciek zmiennych lokalnych i przypadkowe zderzenie (oraz mutację) zmiennych zewnętrznych.
Następnie, gdy widzisz w kodzie odwrotną pętlę for, jest to wskazówka, że jest ona odwrócona z ważnego powodu (być może jednego z powodów opisanych powyżej). A zobaczenie tradycyjnej pętli forward for może oznaczać, że może nastąpić zmiana.
(Jeśli dyskusja intencji nie ma dla ciebie sensu, to ty i twój kod możecie skorzystać z oglądania wykładu Crockforda na temat stylu programowania i mózgu .)
Teraz jest jeszcze lepiej używać do …of!
Toczy się debata na temat tego, for..ofczy forEach()lepiej:
Aby uzyskać maksymalną obsługę przeglądarki, for..ofwymaga wielokrotnego wypełniania iteratorów, co powoduje, że aplikacja działa nieco wolniej, a pobieranie jest nieco większe.
Osobiście staram się używać wszystkiego, co wygląda najłatwiej do odczytania, chyba że wydajność lub ograniczenie stało się poważnym problemem. Więc w dzisiejszych czasach wolę używać for..ofzamiast forEach(), ale zawsze będę używać maplub filterlub findlub w somestosownych przypadkach. (Ze względu na moich kolegów rzadko używam reduce.)
Jak to działa?
for(var i =0; i < array.length; i++){...}// Forwardsfor(var i = array.length; i--;){...}// Reverse
Zauważysz, że i--jest to środkowa klauzula (gdzie zwykle widzimy porównanie), a ostatnia klauzula jest pusta (gdzie zwykle widzimy i++). Oznacza to, że i--jest również wykorzystywany jako warunek kontynuacji. Co najważniejsze, jest on wykonywany i sprawdzany przed każdą iteracją.
Jak można zacząć od array.lengtheksplozji?
Ponieważ i--biegnie przed każdą iteracją, podczas pierwszej iteracji będziemy faktycznie uzyskiwać dostęp do elementu, w array.length - 1którym unika się problemów z elementami Array-poza granicamiundefined .
Dlaczego nie przestaje iterować przed indeksem 0?
Pętla przestanie iterować, gdy warunek i--przejdzie do wartości falsey (gdy da 0).
Sztuczka polega na tym --i, że w przeciwieństwie do i--operatora końcowego , zmniejsza się, iale daje wartość przed zmniejszeniem. Twoja konsola może to wykazać:
> var i = 5; [i, i--, i];
[5, 5, 4]
Więc na końcowej iteracji, I był poprzednio 1 i i--ekspresja zmienia go do 0 , ale w rzeczywistości daje 1 (truthy), a więc stan przechodzi. Przy następnej iteracji i--zmienia się i na -1, ale daje 0 (falsey), co powoduje, że wykonanie natychmiast spada z dolnej części pętli.
W tradycyjnych forwardach for loop i++i ++isą one wymienne (jak podkreśla Douglas Crockford). Jednak w odwrotnej pętli for, ponieważ nasze zmniejszenie jest również naszym wyrażeniem warunku, musimy się trzymać, i--jeśli chcemy przetworzyć element o indeksie 0.
Drobnostki
Niektórzy ludzie lubią narysować małą strzałkę w forpętli zwrotnej i zakończyć mrugnięciem:
for(var i = array.length; i -->0;){
Kredyty trafiają do WYL za pokazanie mi zalet i okropności odwrotności pętli for.
Zapomniałem dodać wzorców . Zapomniałem też wspomnieć, że pętla zwrotna jest znaczącą optymalizacją dla 8-bitowych procesorów, takich jak 6502, gdzie naprawdę dostajesz porównanie za darmo!
joeytwiddle
Tę samą odpowiedź podano tutaj w sposób bardziej zwięzły (na inne pytanie).
joeytwiddle
84
Niektóre języki w stylu C używają foreachdo przechodzenia między wyliczeniami. W JavaScript odbywa się to za pomocą for..instruktury pętli :
var index,
value;for(index in obj){
value = obj[index];}
Jest w tym jakiś haczyk. for..inbędzie przechodzić przez każdy z wymienionych elementów obiektu i członków jego prototypu. Aby uniknąć odczytu wartości dziedziczonych przez prototyp obiektu, wystarczy sprawdzić, czy właściwość należy do obiektu:
for(i in obj){if(obj.hasOwnProperty(i)){//do stuff}}
Ponadto w ECMAScript 5 dodano forEachmetodę, za pomocą Array.prototypektórej można wyliczyć liczbę nad tablicą za pomocą funkcji Calback (wypełnienie znajduje się w dokumentach, więc nadal można go używać w starszych przeglądarkach):
Ważne jest, aby pamiętać, że Array.prototype.forEachnie psuje się po oddzwonieniu false. jQuery i Underscore.js zapewniają własne odmiany, eachaby zapewnić pętle, które mogą być zwarte.
Jak więc wyjść z pętli foreach ECMAScript5, tak jak w przypadku normalnej pętli for lub pętli foreach podobnej do tej w językach C?
Ciaran Gallagher
5
@CiaranG, w JavaScript często spotyka się eachmetody pozwalające return falsena wyjście z pętli, ale forEachnie jest to możliwe. Można użyć flagi zewnętrznej (tj. if (flag) return;, Ale uniemożliwiłoby to wykonanie reszty ciała funkcji, forEachnadal kontynuowałoby iterację po całej kolekcji.
zzzzBov
43
Jeśli chcesz zapętlić tablicę, użyj standardowej forpętli trzyczęściowej .
for(var i =0; i < myArray.length; i++){var arrayItem = myArray[i];}
Możesz uzyskać optymalizację wydajności, buforując myArray.lengthlub iterując wstecz.
for (var i = 0, length = myArray.length; i <length; i ++) powinien to zrobić
Edson Medina
5
@EdsonMedina To również utworzy nową zmienną globalną o nazwie length. ;)
joeytwiddle
4
@joeytwiddle Tak, ale to nie wchodzi w zakres tego postu. W każdym razie utworzysz zmienną globalną i.
Edson Medina
6
@EdsonMedina Przepraszam, że całkowicie się myliłem. Używanie ,po zadaniu nie wprowadza nowego globalnego, więc twoja sugestia jest w porządku ! Myliłem to z powodu innego problemu: użycie =po zadaniu tworzy nowy globalny.
joeytwiddle
Uwaga, zmienna i nie jest lokalna dla pętli. JavaScript nie ma zasięgu blokowego. Prawdopodobnie lepiej jest zadeklarować var i, length, arrayItem;przed pętlą, aby uniknąć tego nieporozumienia.
James
35
Wiem, że to stary post i jest już tyle świetnych odpowiedzi. Dla odrobiny kompletności pomyślałem, że wrzucę inną za pomocą AngularJS . Oczywiście dotyczy to tylko jeśli używasz Angulara, oczywiście, mimo to chciałbym to powiedzieć.
angular.forEachpobiera 2 argumenty i opcjonalnie trzeci argument. Pierwszy argument to obiekt (tablica) do iteracji, drugi argument to funkcja iteracji, a opcjonalny trzeci argument to kontekst obiektu (określany w pętli jako „this”).
Istnieją różne sposoby użycia pętli kątowej forEach. Najprostszym i prawdopodobnie najczęściej używanym jest
var temp =[1,2,3];
angular.forEach(temp,function(item){//item will be each element in the array//do something});
Innym sposobem przydatnym do kopiowania elementów z jednej tablicy do drugiej jest
var temp =[1,2,3];var temp2 =[];
angular.forEach(temp,function(item){this.push(item);//"this" refers to the array passed into the optional third parameter so, in this case, temp2.}, temp2);
Chociaż nie musisz tego robić, możesz po prostu wykonać następujące czynności i jest to odpowiednik poprzedniego przykładu:
Teraz są zalety i wady korzystania z tej angular.forEachfunkcji w przeciwieństwie do wbudowanej forpętli o smaku waniliowym .
Plusy
Łatwa czytelność
Łatwe zapisywanie
Jeśli jest dostępna, angular.forEachużyje pętli ES5 forEach. Teraz przejdę do wydajności w dziale przeciw, ponieważ pętle forEach są znacznie wolniejsze niż pętle for. Mówię o tym jako profesjonalista, ponieważ miło jest być konsekwentnym i znormalizowanym.
Rozważ następujące 2 zagnieżdżone pętle, które robią dokładnie to samo. Powiedzmy, że mamy 2 tablice obiektów, a każdy obiekt zawiera tablicę wyników, z których każda ma właściwość Value, która jest łańcuchem (lub czymkolwiek innym). Powiedzmy, że musimy powtórzyć każdy z wyników, a jeśli są one równe, wykonaj jakąś akcję:
angular.forEach(obj1.results,function(result1){
angular.forEach(obj2.results,function(result2){if(result1.Value=== result2.Value){//do something}});});//exact same with a for loopfor(var i =0; i < obj1.results.length; i++){for(var j =0; j < obj2.results.length; j++){if(obj1.results[i].Value=== obj2.results[j].Value){//do something}}}
To prawda, że jest to bardzo prosty hipotetyczny przykład, ale napisałem potrójne osadzenie dla pętli przy użyciu drugiego podejścia i bardzo trudno było je czytać i pisać o tym.
Cons
Wydajność. angular.forEach, a natywna forEach, w tym przypadku, są o wiele wolniejsze niż normalna forpętla ... około 90% wolniej . Dlatego w przypadku dużych zestawów danych najlepiej trzymać się forpętli natywnej .
Bez przerwy, kontynuacji lub zwrotu pomocy. continuejest faktycznie obsługiwany przez „ wypadek ”, aby kontynuować w angular.forEachprosty sposób wstaw return;instrukcję do funkcji, angular.forEach(array, function(item) { if (someConditionIsTrue) return; });która spowoduje, że będzie kontynuowała działanie dla tej iteracji. Wynika to również z faktu, że natywny forEachnie obsługuje przerwań ani kontynuowania.
Jestem pewien, że istnieją również inne zalety i wady, i dodaj dowolne, które uważasz za stosowne. Wydaje mi się, że jeśli potrzebujesz wydajności, trzymaj się tylko natywnej forpętli dla swoich potrzeb. Ale jeśli twoje zbiory danych są mniejsze i pewna wydajność jest w porządku, aby zrezygnować w zamian za czytelność i zapis, to z całą pewnością wrzuć angular.forEachtego złego chłopca.
function forEach(list,callback){var length = list.length;for(var n =0; n < length; n++){
callback.call(list[n]);}}var myArray =['hello','world'];
forEach(
myArray,function(){
alert(this);// do something});
Prawdopodobnie for(i = 0; i < array.length; i++)pętla nie jest najlepszym wyborem. Dlaczego? Jeśli masz to:
var array =newArray();
array[1]="Hello";
array[7]="World";
array[11]="!";
Metoda będzie wywoływać od array[0]do array[2]. Po pierwsze, będzie to najpierw odwoływać się do zmiennych, których nawet nie masz, po drugie, nie będziesz mieć zmiennych w tablicy, a po trzecie sprawi, że kod będzie pogrubiony. Spójrz tutaj, tego używam:
for(var i in array){var el = array[i];//If you want 'i' to be INT just put parseInt(i)//Do something with el}
A jeśli chcesz, aby była to funkcja, możesz to zrobić:
function foreach(array, call){for(var i in array){
call(array[i]);}}
Jeśli chcesz się złamać, trochę więcej logiki:
function foreach(array, call){for(var i in array){if(call(array[i])==false){break;}}}
Istnieją trzy implementacje foreachw jQuery w następujący sposób.
var a =[3,2];
$(a).each(function(){console.log(this.valueOf())});//Method 1
$.each(a,function(){console.log(this.valueOf())});//Method 2
$.each($(a),function(){console.log(this.valueOf())});//Method 3
Łatwym rozwiązaniem byłoby teraz użycie biblioteki underscore.js . Zapewnia wiele przydatnych narzędzi, takich jak eachi automatycznie deleguje zadanie do rodzimej, forEachjeśli jest dostępna.
W for eachnatywnym JavaScript nie ma żadnej pętli . Możesz użyć bibliotek, aby uzyskać tę funkcjonalność (polecam Underscore.js ), użyj prostej forpętli.
Jest to iterator dla listy NIEDZIELEJ, w której indeks zaczyna się od 0, co jest typowym scenariuszem w przypadku document.getElementsByTagName lub document.querySelectorAll)
function each( fn, data ){if(typeof fn =='string')eval('fn = function(data, i){'+ fn +'}');for(var i=0, L=this.length; i < L; i++)
fn.call(this[i], data, i );returnthis;}Array.prototype.each = each;
Przykłady użycia:
Przykład 1
var arr =[];[1,2,3].each(function(a){ a.push(this*this}, arr);
arr =[1,4,9]
each.call(document.getElementsByTagName('p'),"if( i % 2 == 0) this.className = data;",'red');
Co drugi znacznik p otrzymuje class="red">
Przykład 4
each.call(document.querySelectorAll('p.blue'),function(newClass, i){if( i <20)this.className = newClass;},'green');
I wreszcie pierwsze 20 niebieskich znaczników p zmienia się na zielone
Ostrożnie przy użyciu łańcucha jako funkcji: funkcja jest tworzona poza kontekstem i powinna być używana tylko tam, gdzie masz pewność, że zakres zmiennych jest zmienny. W przeciwnym razie lepiej przekazać funkcje, w których zakres jest bardziej intuicyjny.
Istnieje kilka sposobów na przeglądanie tablicy w JavaScript, jak poniżej:
dla - jest najczęstszy . Pełny blok kodu do zapętlania
var languages =["Java","JavaScript","C#","Python"];var i, len, text;for(i =0, len = languages.length, text =""; i < len; i++){
text += languages[i]+"<br>";}
document.getElementById("example").innerHTML = text;
Pętle funkcjonalne - forEach, map, filter, również reduce(oni pętli funkcji, ale są one wykorzystywane, gdy trzeba zrobić coś ze swojej tablicy itp
// For example, in this case we loop through the number and double them up using the map functionvar numbers =[65,44,12,4];
document.getElementById("example").innerHTML = numbers.map(function(num){return num *2});
Mała korekta: forEachnie jest „funkcjonalną” pętlą, ponieważ nie zwraca nowej Array(w rzeczywistości niczego nie zwraca), po prostu iteruje.
nicholaswmin
19
ECMAScript 5 (wersja JavaScript) do pracy z tablicami:
forEach - Iteruje przez każdy element w tablicy i robi wszystko, czego potrzebujesz z każdym elementem.
['C','D','E'].forEach(function(element, index){
console.log(element +" is #"+(index+1)+" in the musical scale");});// Output// C is the #1 in musical scale// D is the #2 in musical scale// E is the #3 in musical scale
W przypadku, bardziej zainteresowany działaniem na macierzy za pomocą wbudowanej funkcji.
map - Tworzy nową tablicę z wynikiem funkcji zwrotnej. Ta metoda jest przydatna, gdy trzeba sformatować elementy tablicy.
// Let's upper case the items in the array['bob','joe','jen'].map(function(elem){return elem.toUpperCase();});// Output: ['BOB', 'JOE', 'JEN']
redukuj - jak sama nazwa wskazuje, redukuje tablicę do pojedynczej wartości, wywołując daną funkcję przekazującą bieżący element i wynik poprzedniego wykonania.
Nie ma wbudowanej możliwości włamania się forEach. Aby przerwać wykonywanie, skorzystaj z Array#someponiższych instrukcji :
[1,2,3].some(function(number){return number ===1;});
Działa to, ponieważ somezwraca true, gdy tylko dowolne wywołanie zwrotne, wykonane w kolejności tablicowej, zwraca true, powodując zwarcie wykonania pozostałych.
Oryginalna odpowiedź
dla niektórych patrz prototyp macierzy
Chciałbym również dodać to jako kompozycję pętli zwrotnej i powyższą odpowiedź dla kogoś, kto również chciałby tę składnię.
var foo =[object,object,object];for(var i = foo.length, item; item = foo[--i];){
console.log(item);}
Plusy:
Korzyści z tego: masz już takie referencje już w pierwszym, nie będzie trzeba później zadeklarować w innym wierszu. Jest to przydatne, gdy pętla przechodzi przez tablicę obiektów.
Cons:
To się zepsuje, gdy referencja będzie fałszywa - falsey (niezdefiniowany itp.). Może to być jednak zaletą. Utrudniłoby to jednak czytanie. W zależności od przeglądarki można ją „nie” zoptymalizować, aby działała szybciej niż oryginalna.
Restrukturyzacja i użycie operatora rozprzestrzeniania się okazały się całkiem przydatne dla nowych użytkowników ECMAScript 6, ponieważ są bardziej czytelne dla człowieka / estetyczne, chociaż niektórzy weterani JavaScript mogą uważać to za bałagan. Juniorzy lub niektórzy inni ludzie mogą uznać to za przydatne.
Poniższe przykłady wykorzystają for...ofinstrukcję i .forEachmetodę.
Przykłady 6, 7 i 8 mogą być wykorzystywane wszelkie funkcjonalnych, takich jak pętle .map, .filter, .reduce, .sort, .every, .some. Aby uzyskać więcej informacji o tych metodach, sprawdź obiekt Array .
Przykład 1: Normalna for...ofpętla - tutaj nie ma żadnych lew.
let arrSimple =['a','b','c'];for(let letter of arrSimple){
console.log(letter);}
let arrFruits =['apple','orange','banana'];for(let[firstLetter,...restOfTheWord]of arrFruits){// Create a shallow copy using the spread operatorlet[lastLetter]=[...restOfTheWord].reverse();
console.log(firstLetter, lastLetter, restOfTheWord);}
// let arrSimple = ['a', 'b', 'c'];// Instead of keeping an index in `i` as per example `for(let i = 0 ; i<arrSimple.length;i++)`// this example will use a multi-dimensional array of the following format type:// `arrWithIndex: [number, string][]`let arrWithIndex =[[0,'a'],[1,'b'],[2,'c'],];// Same thing can be achieved using `.map` method// let arrWithIndex = arrSimple.map((i, idx) => [idx, i]);// Same thing can be achieved using `Object.entries`// NOTE: `Object.entries` method doesn't work on Internet Explorer unless it's polyfilled// let arrWithIndex = Object.entries(arrSimple);for(let[key, value]of arrWithIndex){
console.log(key, value);}
let arrWithIndex =[[0,'a'],[1,'b'],[2,'c'],];// Not to be confused here, `forEachIndex` is the real index// `mappedIndex` was created by "another user", so you can't really trust it
arrWithIndex.forEach(([mappedIndex, item], forEachIndex)=>{
console.log(forEachIndex, mappedIndex, item);});
let arrWithObjects =[{
name:'Jon',
age:32},{
name:'Elise',
age:33}];// NOTE: Destructuring objects while using shorthand functions// are required to be surrounded by parentheses
arrWithObjects.forEach(({ name, age: aliasForAge })=>{
console.log(name, aliasForAge)});
Najbliższym pomysłem byłoby użycie, Array.forEach()która akceptuje funkcję zamknięcia, która zostanie wykonana dla każdego elementu tablicy.
myArray.forEach((item)=>{// Do something
console.log(item);});
Innym realnym sposobem byłoby użycie, Array.map()które działa w ten sam sposób, ale bierze również wszystkie zwracane wartości i zwraca je w nowej tablicy (zasadniczo mapując każdy element na nowy), na przykład:
Jest to błędne, mapnie powoduje mutacji elementów tablicy, ponieważ zwraca nową tablicę, a elementy tego nowego wynikają z zastosowania funkcji do elementów starej tablicy.
Jesús Franco
Nigdy nie mówiłem, że nie zwraca nowej tablicy, miałem na myśli zmianę zastosowaną przez funkcję. Ale tutaj zmienię to w odpowiedzi.
Ante Jablan Adamović
znowu źle, mapa nie modyfikuje ani nie mutuje wszystkich elementów w oryginalnej tablicy, zaproponowałem i edytuję odpowiedź, podkreślając, że mapa działa z kopią oryginalnych elementów, pozostawiając oryginalną tablicę w ogóle nietkniętą
Jesús Franco
7
Możesz zadzwonić:
forEachwykona iterację nad podaną tablicą i dla każdej iteracji, która będzie miała elementwartość tej iteracji. Jeśli potrzebujesz indeksu, możesz uzyskać bieżący indeks, przekazująci jako drugi parametr w funkcji wywołania zwrotnego forEach.
Foreach jest w zasadzie funkcją wyższego rzędu, która przyjmuje inną funkcję jako parametr.
let theArray=[1,3,2];
theArray.forEach((element)=>{// Use the element of the array
console.log(element)}
Wynik:
132
Możesz także iterować tablicę w ten sposób:
for(let i=0; i<theArray.length; i++){
console.log(i);// i will have the value of each index}
Jeśli chcesz przewijać tablicę obiektów za pomocą funkcji strzałki:
let arr =[{name:'john', age:50},{name:'clark', age:19},{name:'mohan', age:26}];
arr.forEach((person)=>{
console.log('I am '+ person.name +' and I am '+ person.age +' old');})
Składnia lambda zwykle nie działa w przeglądarce Internet Explorer 10 lub niższej.
Zwykle używam
[].forEach.call(arrayName,function(value,index){
console.log("value of the looped element"+ value);
console.log("index of the looped element"+ index);});
Jeśli jesteś fanem jQuery i masz już uruchomiony plik jQuery, powinieneś odwrócić pozycje parametrów indeksu i wartości
$("#ul>li").each(function(**index, value**){
console.log("value of the looped element"+ value);
console.log("index of the looped element"+ index);});
Jeśli masz ogromną tablicę, powinieneś użyć, iteratorsaby zyskać trochę wydajności. Iteratory są własnością niektórych kolekcjach JavaScript (jak Map, Set, String, Array). Nawet for..ofużywa iteratorpod maską.
Iteratory poprawiają wydajność, pozwalając na konsumowanie pojedynczych pozycji na liście, tak jakby były strumieniem. Tym, co wyróżnia iterator, jest sposób przechodzenia przez kolekcję. Inne pętle muszą ładować całą kolekcję z góry, aby wykonać iterację, podczas gdy iterator musi znać tylko bieżącą pozycję w kolekcji.
Dostęp do bieżącego elementu uzyskuje się przez wywołanie metody iteratora next. Następna metoda zwróci valuebieżący element i aboolean aby wskazać, kiedy osiągnąłeś koniec kolekcji. Poniżej znajduje się przykład tworzenia iteratora z tablicy.
Przekształć swoją zwykłą tablicę w iterator, używając następującej values()metody:
const myArr =[2,3,4]let it = myArr.values();
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
Dzisiaj (2019-12-18) Przeprowadzam test na moim systemie macOS 10.13.6 (High Sierra), na Chrome v 79.0, Safari v13.0.4 i Firefox v71.0 (64-bitowy) - wnioski na temat optymalizacji (i mikrooptymalizacji, które zwykle nie warto wprowadzać go do kodu, ponieważ korzyść jest niewielka, ale złożoność kodu rośnie).
Wygląda na to, że tradycyjny for i( Aa ) to dobry wybór do pisania szybkiego kodu we wszystkich przeglądarkach.
Inne rozwiązania, takie jak for-of( Ad ), wszystkie w grupie C. ... są zwykle 2 - 10 (i więcej) razy wolniejsze niż Aa , ale w przypadku małych tablic można je stosować - ze względu na zwiększenie przejrzystości kodu.
Pętle z buforowaną długością tablicy n( Ab, Bb, Be ) są czasami szybsze, a czasem nie. Prawdopodobnie kompilatory automatycznie wykrywają tę sytuację i wprowadzają buforowanie. Różnice prędkości między wersjami z pamięci podręcznej i bez pamięci podręcznej ( Aa, Ba, Bd ) wynoszą około ~ 1%, więc wygląda na to, że wprowadzanie njest mikrooptymalizacją .
i--Jak rozwiązaniach, gdzie rozpoczyna się pętla z ostatniego elementu tablicy ( Ac, BC ) wynosi zwykle ~ 30% wolniej niż rozwiązań typu forward - prawdopodobnie powodem jest droga pamięci procesora obróbki cache - forward pamięci czytaniu jest bardziej optymalne dla buforowania CPU). Zaleca się NIE UŻYWAĆ takich rozwiązań.
Detale
W testach obliczamy sumę elementów tablicy. Przeprowadzam test dla małych tablic (10 elementów) i dużych tablic (1M elementów) i dzielę je na trzy grupy:
Podczas iteracji po tablicy często chcemy osiągnąć jeden z następujących celów:
Chcemy iterować tablicę i utworzyć nową tablicę:
Array.prototype.map
Chcemy iterować tablicę i nie tworzyć nowej tablicy:
Array.prototype.forEach
for..ofpętla
W JavaScript istnieje wiele sposobów na osiągnięcie obu tych celów. Niektóre są jednak wygodniejsze niż inne. Poniżej znajdziesz niektóre najczęściej używane metody (najwygodniejszy IMO) do wykonania iteracji tablicy w JavaScript.
Tworzenie nowej tablicy: Map
map()to funkcja znajdująca się na, Array.prototypektóra może przekształcić każdy element tablicy, a następnie zwraca nową tablicę. map()przyjmuje jako argument funkcję wywołania zwrotnego i działa w następujący sposób:
let arr =[1,2,3,4,5];let newArr = arr.map((element, index, array)=>{return element *2;})
console.log(arr);
console.log(newArr);
Wywołanie zwrotne, które przeszliśmy map()jako argument, jest wykonywane dla każdego elementu. Następnie zwracana jest tablica o takiej samej długości jak tablica oryginalna. W tym nowym elemencie tablicy transformowana jest funkcja wywołania zwrotnego przekazana jako argument do map().
Wyraźną różnicą między mapi innym mechanizmem pętli, takim jak forEachi for..ofpętla, jest to, że mapzwraca nową tablicę i pozostawia nienaruszoną starą tablicę (z wyjątkiem sytuacji, gdy jawnie manipulujesz nią za pomocą myślisplice ).
Zauważ też, że mapwywołanie zwrotne funkcji podaje numer indeksu bieżącej iteracji jako drugi argument. Ponadto, czy trzeci argument zawiera tablicę, na której mapzostała wywołana? Czasami te właściwości mogą być bardzo przydatne.
Pętla za pomocą forEach
forEachjest funkcją, która znajduje się na, Array.prototypektóra przyjmuje funkcję wywołania zwrotnego jako argument. Następnie wykonuje tę funkcję zwrotną dla każdego elementu w tablicy. W przeciwieństwie do map()funkcji, funkcja forEach nie zwraca nic ( undefined). Na przykład:
let arr =[1,2,3,4,5];
arr.forEach((element, index, array)=>{
console.log(element *2);if(index ===4){
console.log(array)}// index, and oldArray are provided as 2nd and 3th argument by the callback})
console.log(arr);
Podobnie jak mapfunkcja, forEachwywołanie zwrotne podaje numer indeksu bieżącej iteracji jako drugi argument. Czy trzeci argument podaje tablicę, na którejforEach została wywołana?
Pętla za pomocą elementów for..of
for..ofPętli przechodzi poprzez każdy element tablicy (albo jakiejkolwiek innej iterowalny obiektu). Działa w następujący sposób:
let arr =[1,2,3,4,5];for(let element of arr){
console.log(element *2);}
W powyższym przykładzie elementoznacza element tablicy i arrjest tablicą, którą chcemy zapętlić. Pamiętaj, że nazwa elementjest dowolna i mogliśmy wybrać dowolną inną nazwę, np. „El” lub coś bardziej deklaratywnego, jeśli ma to zastosowanie.
Nie myl for..inpętli z for..ofpętlą. for..inprzejdzie przez wszystkie wyliczalne właściwości tablicy, podczas gdy for..ofpętla będzie przechodzić tylko przez elementy tablicy. Na przykład:
let arr =[1,2,3,4,5];
arr.foo ='foo';for(let element of arr){
console.log(element);}for(let element in arr){
console.log(element);}
forEach
i nie tylkofor
. jak powiedziano, w c # było trochę inaczej i to mnie zdezorientowało :)i < len
ii++
może to zrobić silnik, a nie tłumacz).Odpowiedzi:
TL; DR
for-in
chyba że używasz go z zabezpieczeniami lub przynajmniej wiesz, dlaczego może cię ugryźć.Twoje najlepsze zakłady są zwykle
for-of
pętli (ES2015 + wyłącznie)Array#forEach
(spec
|MDN
) (lub jego krewnisome
i tak dalej) (tylko ES5 +),for
pętla,for-in
z zabezpieczeniami.Ale jest o wiele więcej do odkrycia, czytaj dalej ...
JavaScript ma potężną semantykę do zapętlania tablic i obiektów podobnych do tablic. Podzieliłem odpowiedź na dwie części: Opcje dla prawdziwych tablic i opcje dla rzeczy, które są po prostu podobne do tablic , takich jak
arguments
obiekt, inne iterowalne obiekty (ES2015 +), kolekcje DOM i tak dalej.Ja szybko zauważyć, że można korzystać z opcji ES2015 teraz , nawet w silnikach ES5 przez transpiling ES2015 do ES5. Wyszukaj „ES2015 transpiling” / „ES6 transpiling”, aby uzyskać więcej ...
Ok, spójrzmy na nasze opcje:
Dla rzeczywistych tablic
Masz trzy opcje w ECMAScript 5 („ES5”), najbardziej obecnie obsługiwana wersja, i dwie kolejne dodane w ECMAScript 2015 („ES2015”, „ES6”):
forEach
i powiązane (ES5 +)for
pętlifor-in
poprawniefor-of
(użyj pośrednio iteratora) (ES2015 +)Detale:
1. Wykorzystanie
forEach
i powiązaneW dowolnym nieco nowoczesnym środowisku (a więc nie IE8), w którym masz dostęp do
Array
funkcji dodanych przez ES5 (bezpośrednio lub za pomocą wielopełniaczy), możesz użyćforEach
(spec
|MDN
):forEach
akceptuje funkcję wywołania zwrotnego i opcjonalnie wartość, która ma być używanathis
podczas wywoływania tego wywołania zwrotnego (nieużywane powyżej). Wywołanie zwrotne jest wywoływane dla każdego wpisu w tablicy, w kolejności, pomijając nieistniejące wpisy w rzadkich tablicach. Chociaż użyłem tylko jednego argumentu powyżej, wywołanie zwrotne jest wywoływane z trzema: wartością każdego wpisu, indeksem tego wpisu i referencją do tablicy, nad którą się iteruje (na wypadek, gdyby twoja funkcja jeszcze tego nie miała) ).O ile nie obsługujesz przestarzałych przeglądarek, takich jak IE8 (które NetApps pokazuje przy nieco ponad 4% udziale w rynku od tego pisania we wrześniu 2016 r.), Możesz z przyjemnością korzystać
forEach
z ogólnej strony internetowej bez cienia wątpliwości . Jeśli potrzebujesz obsługi przestarzałych przeglądarek,forEach
łatwo jest wykonać shimming / polifilling (wyszukaj „es5 shim” dla kilku opcji).forEach
ma tę zaletę, że nie trzeba deklarować zmiennych indeksujących i wartościujących w zakresie zawierającym, ponieważ są one dostarczane jako argumenty do funkcji iteracji, a więc ładnie obejmują tylko tę iterację.Jeśli martwisz się kosztami wykonania wywołania funkcji dla każdego wpisu tablicy, nie przejmuj się; szczegóły .
Dodatkowo
forEach
jest funkcja „pętli przez wszystkie”, ale w ES5 zdefiniowano kilka innych przydatnych funkcji „przeszukiwania tablicy i robienia rzeczy”, w tym:every
(przestaje zapętlać się po pierwszym powrocie wywołania zwrotnegofalse
lub coś falsey)some
(przestaje zapętlać się po pierwszym powrocie wywołania zwrotnegotrue
lub coś prawdziwego)filter
(tworzy nową tablicę zawierającą elementy, do których zwraca funkcja filtrutrue
i pomija te, które zwracafalse
)map
(tworzy nową tablicę na podstawie wartości zwróconych przez wywołanie zwrotne)reduce
(buduje wartość poprzez wielokrotne wywoływanie wywołania zwrotnego, przekazywanie poprzednich wartości; zobacz specyfikację dla szczegółów; przydatne do sumowania zawartości tablicy i wielu innych rzeczy)reduceRight
(jakreduce
, ale działa w kolejności malejącej niż rosnącej)2. Użyj prostej
for
pętliCzasami najlepsze są stare sposoby:
Jeśli długość tablicy nie ulegnie zmianie podczas pętli, i to w wykonaniu wrażliwych kodu (mało prawdopodobne), nieco bardziej skomplikowana wersja chwytając długości do przodu może być malutki nieco szybciej:
I / lub odliczanie wstecz:
Ale w nowoczesnych silnikach JavaScript rzadko trzeba wyciągać ten ostatni kawałek soku.
W wersji ES2015 i nowszych możesz ustawić zmienne indeksu i wartości na lokalne dla
for
pętli:Pokaż fragment kodu
A kiedy to zrobisz, nie tylko
value
, ale równieżindex
jest odtworzony na każdej iteracji pętli, czyli zamknięcia utworzone w ciele pętli zachować odniesienie doindex
(ivalue
) utworzona dla tej konkretnej iteracji:Pokaż fragment kodu
Gdybyś miał pięć div, dostaniesz „Index is: 0”, jeśli klikniesz pierwszy i „Index is: 4”, jeśli klikniesz ostatni. To nie działa, jeśli używasz
var
zamiastlet
.3. Używaj
for-in
poprawnieDostaniesz ludzi mówiących ci, żebyś używał
for-in
, ale nie po tofor-in
jest .for-in
zapętla wyliczalne właściwości obiektu , a nie indeksy tablicy. Zamówienie nie jest gwarantowane , nawet w ES2015 (ES6). ES2015 + nie definiuje zamówienia do właściwości obiektów (przez[[OwnPropertyKeys]]
,[[Enumerate]]
i rzeczy, które ich używają podobaObject.getOwnPropertyKeys
), ale nie określają, żefor-in
pójdzie zlecenia; ES2020 jednak. (Szczegóły w tej drugiej odpowiedzi .)Jedynymi rzeczywistymi przypadkami użycia dla
for-in
tablicy są:Patrząc tylko na pierwszy przykład: możesz użyć
for-in
do odwiedzenia tych rzadkich elementów tablicy, jeśli zastosujesz odpowiednie zabezpieczenia:Zwróć uwagę na trzy kontrole:
Że przedmiot ma swój własny obiekt o tej samej nazwie (nie jeden to dziedziczy od swojego prototypu) oraz
Że kluczem są wszystkie cyfry dziesiętne (np. Normalna postać łańcucha, a nie notacja naukowa), i
Wartość klucza po wymuszeniu na liczbę wynosi <= 2 ^ 32 - 2 (czyli 4 294 967 294). Skąd ta liczba? Jest to część definicji indeksu tablicowego w specyfikacji . Inne liczby (liczby całkowite, liczby ujemne, liczby większe niż 2 ^ 32-2) nie są indeksami tablicowymi. Wynika to z tego, że 2 ^ 32 - 2 sprawia, że najwyższa wartość indeksu jest mniejsza niż 2 ^ 32 - 1 , czyli maksymalna wartość, jaką
length
może mieć tablica . (Np. Długość tablicy mieści się w 32-bitowej liczbie całkowitej bez znaku). (Proponuje RobGowi wskazanie w komentarzu na moim blogu, że mój poprzedni test nie był całkiem poprawny .)Oczywiście nie zrobiłbyś tego w kodzie wbudowanym. Napisziłbyś funkcję narzędzia. Być może:
Pokaż fragment kodu
4. Użyj
for-of
(użyj pośrednio iteratora) (ES2015 +)ES2015 dodał iteratory do JavaScript. Najłatwiejszym sposobem korzystania z iteratorów jest nowa
for-of
instrukcja. To wygląda tak:Pod osłonami pobiera iterator z tablicy i przechodzi przez nią, uzyskując z niej wartości. Nie ma to problemu, który
for-in
ma, ponieważ używa iteratora zdefiniowanego przez obiekt (tablicę), a tablice definiują, że ich iteratory iterują przez swoje wpisy (a nie ich właściwości). Inaczej niżfor-in
w ES5, kolejność odwiedzania wpisów to kolejność numeryczna ich indeksów.5. Użyj jawnie iteratora (ES2015 +)
Czasami możesz chcieć jawnie użyć iteratora . Możesz to również zrobić, chociaż jest to znacznie bardziej niezręczne niż . To wygląda tak:
for-of
Iterator jest obiektem pasującym do definicji Iterator w specyfikacji. Ta
next
metoda zwraca nowy obiekt wyniku za każdym razem, gdy go wywołujesz. Wynikowy obiekt ma właściwość, któradone
mówi nam, czy zostało wykonane, oraz właściwośćvalue
o wartości dla tej iteracji. (done
jest opcjonalny, jeśli byłbyfalse
,value
jest opcjonalny, gdyby byłundefined
).Znaczenie
value
różni się w zależności od iteratora; tablice obsługują (co najmniej) trzy funkcje zwracające iteratory:values()
: Tego użyłem powyżej. Zwraca iteracyjnej, gdzie każdyvalue
jest wpis tablicy w tej iteracji ("a"
,"b"
i"c"
w tym przykładzie wcześniej).keys()
: Zwraca iterator, w którym każdyvalue
jest kluczem do tej iteracji (więc dla naszegoa
powyższego byłoby"0"
to"1"
wtedy"2"
).entries()
: Zwraca iterator, w którym każdyvalue
jest tablicą w formie[key, value]
dla tej iteracji.Dla obiektów podobnych do tablicy
Oprócz prawdziwych tablic istnieją również obiekty podobne do tablic, które mają
length
właściwość i właściwości o nazwach numerycznych:NodeList
instancje,arguments
obiekt itp. Jak przeglądamy ich zawartość?Użyj dowolnej z powyższych opcji dla tablic
Przynajmniej niektóre, a być może większość lub nawet wszystkie powyższe podejścia tablicowe często stosuje się równie dobrze do obiektów podobnych do tablicy:
Zastosowanie
forEach
i powiązane (ES5 +)Różne włączone funkcje
Array.prototype
są „celowo ogólne” i zwykle można ich używać na obiektach podobnych do tablicy za pomocąFunction#call
lubFunction#apply
. (Zobacz zastrzeżenie dotyczące obiektów dostarczonych przez hosta na końcu tej odpowiedzi, ale jest to rzadki problem).Załóżmy, że chcesz skorzystać
forEach
naNode
„schildNodes
nieruchomości. Zrobiłbyś to:Jeśli zamierzasz to często robić, możesz pobrać kopię odwołania do funkcji do zmiennej w celu ponownego wykorzystania, np .:
Użyj prostej
for
pętliOczywiście prosta
for
pętla dotyczy obiektów podobnych do tablicy.Używaj
for-in
poprawniefor-in
z tymi samymi zabezpieczeniami, co w przypadku tablicy, powinien również działać z obiektami podobnymi do tablicy; może mieć zastosowanie zastrzeżenie dotyczące obiektów dostarczonych przez hosta w punkcie 1 powyżej.Użyj
for-of
(użyj pośrednio iteratora) (ES2015 +)for-of
używa iteratora dostarczonego przez obiekt (jeśli istnieje). Obejmuje to obiekty dostarczone przez hosta. Na przykład specyfikacja dlaNodeList
odquerySelectorAll
została zaktualizowana w celu obsługi iteracji. Specyfikacja dlaHTMLCollection
odgetElementsByTagName
nie była.Użyj jawnie iteratora (ES2015 +)
Zobacz # 4.
Utwórz prawdziwą tablicę
Innym razem możesz chcieć przekonwertować obiekt podobny do tablicy na prawdziwą tablicę. Jest to zaskakująco łatwe:
Użyj
slice
metody tablicMożemy użyć
slice
metody tablic, która podobnie jak inne metody wspomniane powyżej, jest „celowo ogólna” i dlatego może być stosowana z obiektami podobnymi do tablic, takimi jak to:Na przykład, jeśli chcemy przekonwertować
NodeList
tablicę na prawdziwą, moglibyśmy to zrobić:Zobacz zastrzeżenie dotyczące obiektów udostępnianych przez hosta poniżej. W szczególności zauważ, że to się nie powiedzie w IE8 i wcześniejszych wersjach, co nie pozwala na używanie obiektów dostarczonych przez hosta w
this
ten sposób.Użyj składni spread (
...
)Możliwe jest także użycie składni rozproszonej ES2015 z silnikami JavaScript obsługującymi tę funkcję. Podobnie
for-of
, używa iteratora dostarczonego przez obiekt (patrz # 4 w poprzedniej sekcji):Na przykład, jeśli chcemy przekonwertować
NodeList
tablicę na prawdziwą, przy składni rozproszonej staje się to dość zwięzłe:Posługiwać się
Array.from
Array.from
(specyfikacja) | (MDN) (ES2015 +, ale łatwo wypełniany) tworzy tablicę z obiektu podobnego do tablicy, opcjonalnie przepuszczając najpierw wpisy przez funkcję odwzorowania. Więc:Lub jeśli chcesz uzyskać tablicę nazw znaczników elementów z daną klasą, skorzystaj z funkcji mapowania:
Zastrzeżenie dotyczące obiektów udostępnianych przez hosta
Jeśli używasz
Array.prototype
funkcji z obiektami tablicowymi udostępnianymi przez hosta (listami DOM i innymi rzeczami udostępnianymi przez przeglądarkę, a nie z silnika JavaScript), musisz koniecznie przetestować w środowiskach docelowych, aby upewnić się, że obiekt dostarczony przez hosta działa poprawnie . Większość zachowuje się poprawnie (teraz), ale ważne jest, aby przetestować. Powodem jest to, że większośćArray.prototype
metod, których prawdopodobnie chcesz użyć, opiera się na obiekcie dostarczonym przez hosta, dając uczciwą odpowiedź na[[HasProperty]]
operację abstrakcyjną . W tym momencie przeglądarki wykonują bardzo dobrą robotę, ale specyfikacja 5.1 pozwoliła na to, że obiekt udostępniony przez hosta może nie być uczciwy. Znajduje się w §8.6.2 , kilka akapitów poniżej dużej tabeli blisko początku tej sekcji), gdzie jest napisane:(Nie może znaleźć równoważne słownictwa w ES2015 specyfikacji, ale to wiąże się jeszcze w przypadku). Ponownie, w tym piśmie wspólnego gospodarza pod warunkiem, tablicowej obiektów przeglądarkami [
NodeList
przypadkach, na przykład] wykonania uchwytu[[HasProperty]]
poprawnie, ale ważne jest, aby przetestować).źródło
.forEach
nie można tego skutecznie złamać. Musisz rzucić wyjątek, aby wykonać przerwę.some
. (Wolałbym również pozwolić na łamanieforEach
, ale oni, hmm, nie pytali mnie; ;-)),
pok=0
, a nie;
. Pamiętaj, że programowanie to wiele rzeczy, z których jedną jest zwrócenie szczególnej uwagi na szczegóły ... :-)length
nie jest to metoda). :-)Uwaga : ta odpowiedź jest beznadziejnie nieaktualna. Aby uzyskać bardziej nowoczesne podejście, spójrz na metody dostępne w tablicy . Metodami zainteresowania mogą być:
Standardowym sposobem iteracji tablicy w JavaScript jest
for
pętla waniliowa :Zauważ jednak, że takie podejście jest dobre tylko wtedy, gdy masz gęstą tablicę, a każdy indeks jest zajęty przez element. Jeśli tablica jest rzadka, to przy takim podejściu możesz napotkać problemy z wydajnością, ponieważ będziesz iterował wiele indeksów, które tak naprawdę nie istnieją w tablicy. W takim przypadku
for .. in
lepszym pomysłem może być pętla. Jednakże , należy użyć odpowiednich zabezpieczeń w celu zapewnienia, że tylko pożądanych właściwości tablicy (czyli elementy tablicy) są rozpatrzony, ponieważfor..in
-loop zostaną również wymienione w starszych przeglądarkach, czy dodatkowe właściwości są definiowane jakoenumerable
.W ECMAScript 5 na prototypie macierzy będzie metoda forEach, ale nie jest ona obsługiwana w starszych przeglądarkach. Aby móc konsekwentnie z niego korzystać, musisz mieć środowisko, które go obsługuje (na przykład Node.js dla JavaScript po stronie serwera), lub użyć „Polyfill”. Wielokrotne wypełnianie tej funkcji jest jednak trywialne, a ponieważ ułatwia odczytanie kodu, dobrym rozwiązaniem jest włączenie wielokrotnego wypełniania.
źródło
for(instance in objArray)
nie jest prawidłowe użycie? wydaje mi się to prostsze, ale słyszę, że mówisz o nim jako o niewłaściwym sposobie użycia?var
. Gdybyśmy użyli średnika,element
bylibyśmy zadeklarowani w zakresie globalnym (a raczej JSHint krzyczałby na nas, zanim dotarłby do produkcji).Jeśli korzystasz z biblioteki jQuery , możesz użyć jQuery.each :
EDYTOWAĆ :
Jak na pytanie, użytkownik chce kodu w javascript zamiast jquery, więc edycja jest
źródło
Pętla do tyłu
Myślę, że odwrotność pętli for zasługuje na wzmiankę tutaj:
Zalety:
len
zmiennej tymczasowej ani porównywać jejarray.length
przy każdej iteracji, z których każda może być drobiazgową optymalizacją.array[i]
), wówczas pętla do przodu pominie element, który przesunął się w lewo do pozycji i , lub ponownie przetworzy i- ty element, który był przesunął się w prawo. W tradycyjnej pętli for można aktualizować i, aby wskazywać następny element, który wymaga przetworzenia - 1, ale po prostu odwrócenie kierunku iteracji jest często prostszym i bardziej eleganckim rozwiązaniem .forEach()
z ES6for ... of
.Niedogodności:
Czy powinienem go zawsze używać?
Niektórzy programiści domyślnie używają pętli zwrotnej dla pętli domyślnej , chyba że istnieje uzasadniony powód, aby pętli do przodu.
Chociaż wzrost wydajności jest zwykle niewielki, to rodzaj krzyków:
Jednak w praktyce nie jest to wiarygodne wskazanie zamiaru, ponieważ jest nie do odróżnienia od sytuacji, w których dbasz o porządek i naprawdę potrzebujesz zapętlić w odwrotnej kolejności. Tak więc w rzeczywistości potrzebny byłby inny konstrukt, aby dokładnie wyrazić zamiar „nie przejmuj się”, coś, co jest obecnie niedostępne w większości języków, w tym ECMAScript, ale które można nazwać na przykład
forEachUnordered()
.Jeśli kolejność nie ma znaczenia, a wydajność jest istotna (w najbardziej wewnętrznej pętli gry lub silnika animacji), może być dopuszczalne użycie odwrotnej pętli for jako wzorca. Pamiętaj tylko, że zobaczenie odwrotnej pętli for w istniejącym kodzie niekoniecznie oznacza, że kolejność nie ma znaczenia!
Lepiej było użyć forEach ()
Zasadniczo w przypadku kodu wyższego poziomu, w którym większe znaczenie mają przejrzystość i bezpieczeństwo , wcześniej zalecałem używanie
Array::forEach
jako domyślnego wzorca zapętlania (chociaż obecnie wolę używaćfor..of
). Powody, dla których woliszforEach
od pętli zwrotnej są:for
iwhile
pętle).Następnie, gdy widzisz w kodzie odwrotną pętlę for, jest to wskazówka, że jest ona odwrócona z ważnego powodu (być może jednego z powodów opisanych powyżej). A zobaczenie tradycyjnej pętli forward for może oznaczać, że może nastąpić zmiana.
(Jeśli dyskusja intencji nie ma dla ciebie sensu, to ty i twój kod możecie skorzystać z oglądania wykładu Crockforda na temat stylu programowania i mózgu .)
Teraz jest jeszcze lepiej używać do …of!
Toczy się debata na temat tego,
for..of
czyforEach()
lepiej:Aby uzyskać maksymalną obsługę przeglądarki,
for..of
wymaga wielokrotnego wypełniania iteratorów, co powoduje, że aplikacja działa nieco wolniej, a pobieranie jest nieco większe.Z tego powodu (i aby zachęcić do używania
map
ifilter
), niektóre poradniki stylu front-endfor..of
całkowicie blokują !Powyższe obawy nie dotyczą aplikacji Node.js, które
for..of
są teraz dobrze obsługiwane.A ponadto
await
nie działa w środkuforEach()
. W tym przypadku użyciefor..of
jest najwyraźniejszym wzorem .Osobiście staram się używać wszystkiego, co wygląda najłatwiej do odczytania, chyba że wydajność lub ograniczenie stało się poważnym problemem. Więc w dzisiejszych czasach wolę używać
for..of
zamiastforEach()
, ale zawsze będę używaćmap
lubfilter
lubfind
lub wsome
stosownych przypadkach. (Ze względu na moich kolegów rzadko używamreduce
.)Jak to działa?
Zauważysz, że
i--
jest to środkowa klauzula (gdzie zwykle widzimy porównanie), a ostatnia klauzula jest pusta (gdzie zwykle widzimyi++
). Oznacza to, żei--
jest również wykorzystywany jako warunek kontynuacji. Co najważniejsze, jest on wykonywany i sprawdzany przed każdą iteracją.Jak można zacząć od
array.length
eksplozji?Ponieważ
i--
biegnie przed każdą iteracją, podczas pierwszej iteracji będziemy faktycznie uzyskiwać dostęp do elementu, warray.length - 1
którym unika się problemów zelementami Array-poza granicamiundefined
.Dlaczego nie przestaje iterować przed indeksem 0?
Pętla przestanie iterować, gdy warunek
i--
przejdzie do wartości falsey (gdy da 0).Sztuczka polega na tym
--i
, że w przeciwieństwie doi--
operatora końcowego , zmniejsza się,i
ale daje wartość przed zmniejszeniem. Twoja konsola może to wykazać:> var i = 5; [i, i--, i];
[5, 5, 4]
Więc na końcowej iteracji, I był poprzednio 1 i
i--
ekspresja zmienia go do 0 , ale w rzeczywistości daje 1 (truthy), a więc stan przechodzi. Przy następnej iteracjii--
zmienia się i na -1, ale daje 0 (falsey), co powoduje, że wykonanie natychmiast spada z dolnej części pętli.W tradycyjnych forwardach for loop
i++
i++i
są one wymienne (jak podkreśla Douglas Crockford). Jednak w odwrotnej pętli for, ponieważ nasze zmniejszenie jest również naszym wyrażeniem warunku, musimy się trzymać,i--
jeśli chcemy przetworzyć element o indeksie 0.Drobnostki
Niektórzy ludzie lubią narysować małą strzałkę w
for
pętli zwrotnej i zakończyć mrugnięciem:Kredyty trafiają do WYL za pokazanie mi zalet i okropności odwrotności pętli for.
źródło
Niektóre języki w stylu C używają
foreach
do przechodzenia między wyliczeniami. W JavaScript odbywa się to za pomocąfor..in
struktury pętli :Jest w tym jakiś haczyk.
for..in
będzie przechodzić przez każdy z wymienionych elementów obiektu i członków jego prototypu. Aby uniknąć odczytu wartości dziedziczonych przez prototyp obiektu, wystarczy sprawdzić, czy właściwość należy do obiektu:Ponadto w ECMAScript 5 dodano
forEach
metodę, za pomocąArray.prototype
której można wyliczyć liczbę nad tablicą za pomocą funkcji Calback (wypełnienie znajduje się w dokumentach, więc nadal można go używać w starszych przeglądarkach):Ważne jest, aby pamiętać, że
Array.prototype.forEach
nie psuje się po oddzwonieniufalse
. jQuery i Underscore.js zapewniają własne odmiany,each
aby zapewnić pętle, które mogą być zwarte.źródło
each
metody pozwalającereturn false
na wyjście z pętli, aleforEach
nie jest to możliwe. Można użyć flagi zewnętrznej (tj.if (flag) return;
, Ale uniemożliwiłoby to wykonanie reszty ciała funkcji,forEach
nadal kontynuowałoby iterację po całej kolekcji.Jeśli chcesz zapętlić tablicę, użyj standardowej
for
pętli trzyczęściowej .Możesz uzyskać optymalizację wydajności, buforując
myArray.length
lub iterując wstecz.źródło
length
. ;),
po zadaniu nie wprowadza nowego globalnego, więc twoja sugestia jest w porządku ! Myliłem to z powodu innego problemu: użycie=
po zadaniu tworzy nowy globalny.var i, length, arrayItem;
przed pętlą, aby uniknąć tego nieporozumienia.Wiem, że to stary post i jest już tyle świetnych odpowiedzi. Dla odrobiny kompletności pomyślałem, że wrzucę inną za pomocą AngularJS . Oczywiście dotyczy to tylko jeśli używasz Angulara, oczywiście, mimo to chciałbym to powiedzieć.
angular.forEach
pobiera 2 argumenty i opcjonalnie trzeci argument. Pierwszy argument to obiekt (tablica) do iteracji, drugi argument to funkcja iteracji, a opcjonalny trzeci argument to kontekst obiektu (określany w pętli jako „this”).Istnieją różne sposoby użycia pętli kątowej forEach. Najprostszym i prawdopodobnie najczęściej używanym jest
Innym sposobem przydatnym do kopiowania elementów z jednej tablicy do drugiej jest
Chociaż nie musisz tego robić, możesz po prostu wykonać następujące czynności i jest to odpowiednik poprzedniego przykładu:
Teraz są zalety i wady korzystania z tej
angular.forEach
funkcji w przeciwieństwie do wbudowanejfor
pętli o smaku waniliowym .Plusy
angular.forEach
użyje pętli ES5 forEach. Teraz przejdę do wydajności w dziale przeciw, ponieważ pętle forEach są znacznie wolniejsze niż pętle for. Mówię o tym jako profesjonalista, ponieważ miło jest być konsekwentnym i znormalizowanym.Rozważ następujące 2 zagnieżdżone pętle, które robią dokładnie to samo. Powiedzmy, że mamy 2 tablice obiektów, a każdy obiekt zawiera tablicę wyników, z których każda ma właściwość Value, która jest łańcuchem (lub czymkolwiek innym). Powiedzmy, że musimy powtórzyć każdy z wyników, a jeśli są one równe, wykonaj jakąś akcję:
To prawda, że jest to bardzo prosty hipotetyczny przykład, ale napisałem potrójne osadzenie dla pętli przy użyciu drugiego podejścia i bardzo trudno było je czytać i pisać o tym.
Cons
angular.forEach
, a natywnaforEach
, w tym przypadku, są o wiele wolniejsze niż normalnafor
pętla ... około 90% wolniej . Dlatego w przypadku dużych zestawów danych najlepiej trzymać sięfor
pętli natywnej .continue
jest faktycznie obsługiwany przez „ wypadek ”, aby kontynuować wangular.forEach
prosty sposób wstawreturn;
instrukcję do funkcji,angular.forEach(array, function(item) { if (someConditionIsTrue) return; });
która spowoduje, że będzie kontynuowała działanie dla tej iteracji. Wynika to również z faktu, że natywnyforEach
nie obsługuje przerwań ani kontynuowania.Jestem pewien, że istnieją również inne zalety i wady, i dodaj dowolne, które uważasz za stosowne. Wydaje mi się, że jeśli potrzebujesz wydajności, trzymaj się tylko natywnej
for
pętli dla swoich potrzeb. Ale jeśli twoje zbiory danych są mniejsze i pewna wydajność jest w porządku, aby zrezygnować w zamian za czytelność i zapis, to z całą pewnością wrzućangular.forEach
tego złego chłopca.źródło
Jeśli nie masz nic przeciwko opróżnieniu tablicy:
x
będzie zawierać ostatnią wartośćy
i zostanie usunięty z tablicy. Możesz także użyć,shift()
który da i usunie pierwszy elementy
.źródło
[1, 2, undefined, 3]
.[1, 2, 0, 3]
lub[true, true, false, true]
ForEach realizacja ( patrz w jsFiddle ):
źródło
Prawdopodobnie
for(i = 0; i < array.length; i++)
pętla nie jest najlepszym wyborem. Dlaczego? Jeśli masz to:Metoda będzie wywoływać od
array[0]
doarray[2]
. Po pierwsze, będzie to najpierw odwoływać się do zmiennych, których nawet nie masz, po drugie, nie będziesz mieć zmiennych w tablicy, a po trzecie sprawi, że kod będzie pogrubiony. Spójrz tutaj, tego używam:A jeśli chcesz, aby była to funkcja, możesz to zrobić:
Jeśli chcesz się złamać, trochę więcej logiki:
Przykład:
Zwraca:
źródło
Istnieją trzy implementacje
foreach
w jQuery w następujący sposób.źródło
Od ECMAScript 6:
Gdzie
of
unika osobliwości związanych zin
i sprawia, że działa jakfor
pętla dowolnego innego języka ilet
wiążei
w pętli, a nie w obrębie funkcji.Nawiasy klamrowe (
{}
) można pominąć, gdy istnieje tylko jedno polecenie (np. W powyższym przykładzie).źródło
Łatwym rozwiązaniem byłoby teraz użycie biblioteki underscore.js . Zapewnia wiele przydatnych narzędzi, takich jak
each
i automatycznie deleguje zadanie do rodzimej,forEach
jeśli jest dostępna.Przykładem działania CodePen jest:
Zobacz też
Array.prototype.forEach()
.for each (variable in object)
jest przestarzały jako część standardu ECMA-357 ( EAX ).for (variable of object)
jako części propozycji Harmony (ECMAScript 6).źródło
W
for each
natywnym JavaScript nie ma żadnej pętli . Możesz użyć bibliotek, aby uzyskać tę funkcjonalność (polecam Underscore.js ), użyj prostejfor
pętli.Należy jednak pamiętać, że mogą istnieć powody, aby używać jeszcze prostszej
for
pętli (patrz pytanie Przepełnienie stosu Dlaczego używanie „dla… w” z iteracją tablicy jest tak złym pomysłem? )źródło
Jest to iterator dla listy NIEDZIELEJ, w której indeks zaczyna się od 0, co jest typowym scenariuszem w przypadku document.getElementsByTagName lub document.querySelectorAll)
Przykłady użycia:
Przykład 1
Przykład nr 2
Każdy tag p dostaje się
class="blue"
Przykład nr 3
Co drugi znacznik p otrzymuje
class="red"
>Przykład 4
I wreszcie pierwsze 20 niebieskich znaczników p zmienia się na zielone
Ostrożnie przy użyciu łańcucha jako funkcji: funkcja jest tworzona poza kontekstem i powinna być używana tylko tam, gdzie masz pewność, że zakres zmiennych jest zmienny. W przeciwnym razie lepiej przekazać funkcje, w których zakres jest bardziej intuicyjny.
źródło
Istnieje kilka sposobów na przeglądanie tablicy w JavaScript, jak poniżej:
dla - jest najczęstszy . Pełny blok kodu do zapętlania
while - pętla, gdy warunek się skończy. To wydaje się być najszybszą pętlą
do / while - również pętla przez blok kodu, gdy warunek jest spełniony, uruchomi się przynajmniej raz
Pętle funkcjonalne -
forEach
,map
,filter
, równieżreduce
(oni pętli funkcji, ale są one wykorzystywane, gdy trzeba zrobić coś ze swojej tablicy itpAby uzyskać więcej informacji i przykładów dotyczących programowania funkcjonalnego na tablicach, zobacz wpis na blogu Programowanie funkcjonalne w JavaScript: mapuj, filtruj i zmniejszaj .
źródło
forEach
nie jest „funkcjonalną” pętlą, ponieważ nie zwraca nowejArray
(w rzeczywistości niczego nie zwraca), po prostu iteruje.ECMAScript 5 (wersja JavaScript) do pracy z tablicami:
forEach - Iteruje przez każdy element w tablicy i robi wszystko, czego potrzebujesz z każdym elementem.
W przypadku, bardziej zainteresowany działaniem na macierzy za pomocą wbudowanej funkcji.
map - Tworzy nową tablicę z wynikiem funkcji zwrotnej. Ta metoda jest przydatna, gdy trzeba sformatować elementy tablicy.
redukuj - jak sama nazwa wskazuje, redukuje tablicę do pojedynczej wartości, wywołując daną funkcję przekazującą bieżący element i wynik poprzedniego wykonania.
every - Zwraca true lub false, jeśli wszystkie elementy w tablicy przejdą test w funkcji zwrotnej.
filter - bardzo podobny do każdego z wyjątkiem tego, że filter zwraca tablicę z elementami, które zwracają wartość true dla danej funkcji.
źródło
Nie ma wbudowanej możliwości włamania się
forEach
. Aby przerwać wykonywanie, skorzystaj zArray#some
poniższych instrukcji :Działa to, ponieważ
some
zwraca true, gdy tylko dowolne wywołanie zwrotne, wykonane w kolejności tablicowej, zwraca true, powodując zwarcie wykonania pozostałych. Oryginalna odpowiedź dla niektórych patrz prototyp macierzyźródło
Chciałbym również dodać to jako kompozycję pętli zwrotnej i powyższą odpowiedź dla kogoś, kto również chciałby tę składnię.
Plusy:
Korzyści z tego: masz już takie referencje już w pierwszym, nie będzie trzeba później zadeklarować w innym wierszu. Jest to przydatne, gdy pętla przechodzi przez tablicę obiektów.
Cons:
To się zepsuje, gdy referencja będzie fałszywa - falsey (niezdefiniowany itp.). Może to być jednak zaletą. Utrudniłoby to jednak czytanie. W zależności od przeglądarki można ją „nie” zoptymalizować, aby działała szybciej niż oryginalna.
źródło
jQuery sposób za pomocą
$.map
:źródło
[undefined, 2, undefined, 4, undefined, 6, undefined]
.Używanie pętli z destrukcją ECMAScript 6 i operatorem rozprzestrzeniania
Restrukturyzacja i użycie operatora rozprzestrzeniania się okazały się całkiem przydatne dla nowych użytkowników ECMAScript 6, ponieważ są bardziej czytelne dla człowieka / estetyczne, chociaż niektórzy weterani JavaScript mogą uważać to za bałagan. Juniorzy lub niektórzy inni ludzie mogą uznać to za przydatne.
Przykład 1: Normalna
for...of
pętla - tutaj nie ma żadnych lew.Przykład 2: Podziel słowa na znaki
Przykład 3: Pętla za pomocą
key
a ivalue
Przykład 4: Pobierz właściwości obiektu
Przykład 5: Uzyskaj właściwości głębokiego obiektu tego, czego potrzebujesz
Przykład 6: Czy użyto przykładu 3 z
.forEach
Przykład 7: Czy użyto przykładu 4 z
.forEach
Przykład 8: Czy w przykładzie 5 użyto
.forEach
źródło
Najbliższym pomysłem byłoby użycie,
Array.forEach()
która akceptuje funkcję zamknięcia, która zostanie wykonana dla każdego elementu tablicy.Innym realnym sposobem byłoby użycie,
Array.map()
które działa w ten sam sposób, ale bierze również wszystkie zwracane wartości i zwraca je w nowej tablicy (zasadniczo mapując każdy element na nowy), na przykład:źródło
map
nie powoduje mutacji elementów tablicy, ponieważ zwraca nową tablicę, a elementy tego nowego wynikają z zastosowania funkcji do elementów starej tablicy.Możesz zadzwonić:
forEach
wykona iterację nad podaną tablicą i dla każdej iteracji, która będzie miałaelement
wartość tej iteracji. Jeśli potrzebujesz indeksu, możesz uzyskać bieżący indeks, przekazująci
jako drugi parametr w funkcji wywołania zwrotnego forEach.Foreach jest w zasadzie funkcją wyższego rzędu, która przyjmuje inną funkcję jako parametr.
Wynik:
Możesz także iterować tablicę w ten sposób:
źródło
Jeśli chcesz przewijać tablicę obiektów za pomocą funkcji strzałki:
źródło
Składnia lambda zwykle nie działa w przeglądarce Internet Explorer 10 lub niższej.
Zwykle używam
Jeśli jesteś fanem jQuery i masz już uruchomiony plik jQuery, powinieneś odwrócić pozycje parametrów indeksu i wartości
źródło
Jeśli masz ogromną tablicę, powinieneś użyć,
iterators
aby zyskać trochę wydajności. Iteratory są własnością niektórych kolekcjach JavaScript (jakMap
,Set
,String
,Array
). Nawetfor..of
używaiterator
pod maską.Iteratory poprawiają wydajność, pozwalając na konsumowanie pojedynczych pozycji na liście, tak jakby były strumieniem. Tym, co wyróżnia iterator, jest sposób przechodzenia przez kolekcję. Inne pętle muszą ładować całą kolekcję z góry, aby wykonać iterację, podczas gdy iterator musi znać tylko bieżącą pozycję w kolekcji.
Dostęp do bieżącego elementu uzyskuje się przez wywołanie metody iteratora
next
. Następna metoda zwrócivalue
bieżący element i aboolean
aby wskazać, kiedy osiągnąłeś koniec kolekcji. Poniżej znajduje się przykład tworzenia iteratora z tablicy.Przekształć swoją zwykłą tablicę w iterator, używając następującej
values()
metody:Możesz również przekształcić swoją zwykłą tablicę w iterator, wykonując
Symbol.iterator
następujące czynności:Możesz również przekształcić swojego zwykłego
array
witerator
taki:UWAGA :
iterable
domyślnie. Użyjfor..in
w takim przypadku, ponieważ zamiast wartości działa z kluczami.Możesz przeczytać więcej o
iteration protocol
tutaj .źródło
Jeśli chcesz użyć
forEach()
, będzie wyglądać jak:Jeśli chcesz użyć
for()
, będzie wyglądać jak:źródło
Zgodnie z nową zaktualizowaną funkcją ECMAScript 6 (ES6) i ECMAScript 2015 można używać następujących opcji z pętlami:
źródło
Wydajność
Dzisiaj (2019-12-18) Przeprowadzam test na moim systemie macOS 10.13.6 (High Sierra), na Chrome v 79.0, Safari v13.0.4 i Firefox v71.0 (64-bitowy) - wnioski na temat optymalizacji (i mikrooptymalizacji, które zwykle nie warto wprowadzać go do kodu, ponieważ korzyść jest niewielka, ale złożoność kodu rośnie).
Wygląda na to, że tradycyjny
for i
( Aa ) to dobry wybór do pisania szybkiego kodu we wszystkich przeglądarkach.Inne rozwiązania, takie jak
for-of
( Ad ), wszystkie w grupie C. ... są zwykle 2 - 10 (i więcej) razy wolniejsze niż Aa , ale w przypadku małych tablic można je stosować - ze względu na zwiększenie przejrzystości kodu.Pętle z buforowaną długością tablicy
n
( Ab, Bb, Be ) są czasami szybsze, a czasem nie. Prawdopodobnie kompilatory automatycznie wykrywają tę sytuację i wprowadzają buforowanie. Różnice prędkości między wersjami z pamięci podręcznej i bez pamięci podręcznej ( Aa, Ba, Bd ) wynoszą około ~ 1%, więc wygląda na to, że wprowadzanien
jest mikrooptymalizacją .i--
Jak rozwiązaniach, gdzie rozpoczyna się pętla z ostatniego elementu tablicy ( Ac, BC ) wynosi zwykle ~ 30% wolniej niż rozwiązań typu forward - prawdopodobnie powodem jest droga pamięci procesora obróbki cache - forward pamięci czytaniu jest bardziej optymalne dla buforowania CPU). Zaleca się NIE UŻYWAĆ takich rozwiązań.Detale
W testach obliczamy sumę elementów tablicy. Przeprowadzam test dla małych tablic (10 elementów) i dużych tablic (1M elementów) i dzielę je na trzy grupy:
for
testywhile
testyPokaż fragment kodu
Wyniki dla różnych przeglądarek
Wyniki dla wszystkich testowanych przeglądarek
przeglądarki **
Tablica z 10 elementami
Wyniki dla Chrome. Możesz wykonać test na swoim komputerze tutaj .
Tablica z 1 000 000 elementów
Wyniki dla Chrome. Możesz wykonać test na swoim komputerze tutaj
źródło
Podsumowanie:
Podczas iteracji po tablicy często chcemy osiągnąć jeden z następujących celów:
Chcemy iterować tablicę i utworzyć nową tablicę:
Array.prototype.map
Chcemy iterować tablicę i nie tworzyć nowej tablicy:
Array.prototype.forEach
for..of
pętlaW JavaScript istnieje wiele sposobów na osiągnięcie obu tych celów. Niektóre są jednak wygodniejsze niż inne. Poniżej znajdziesz niektóre najczęściej używane metody (najwygodniejszy IMO) do wykonania iteracji tablicy w JavaScript.
Tworzenie nowej tablicy:
Map
map()
to funkcja znajdująca się na,Array.prototype
która może przekształcić każdy element tablicy, a następnie zwraca nową tablicę.map()
przyjmuje jako argument funkcję wywołania zwrotnego i działa w następujący sposób:Wywołanie zwrotne, które przeszliśmy
map()
jako argument, jest wykonywane dla każdego elementu. Następnie zwracana jest tablica o takiej samej długości jak tablica oryginalna. W tym nowym elemencie tablicy transformowana jest funkcja wywołania zwrotnego przekazana jako argument domap()
.Wyraźną różnicą między
map
i innym mechanizmem pętli, takim jakforEach
ifor..of
pętla, jest to, żemap
zwraca nową tablicę i pozostawia nienaruszoną starą tablicę (z wyjątkiem sytuacji, gdy jawnie manipulujesz nią za pomocą myślisplice
).Zauważ też, że
map
wywołanie zwrotne funkcji podaje numer indeksu bieżącej iteracji jako drugi argument. Ponadto, czy trzeci argument zawiera tablicę, na którejmap
została wywołana? Czasami te właściwości mogą być bardzo przydatne.Pętla za pomocą
forEach
forEach
jest funkcją, która znajduje się na,Array.prototype
która przyjmuje funkcję wywołania zwrotnego jako argument. Następnie wykonuje tę funkcję zwrotną dla każdego elementu w tablicy. W przeciwieństwie domap()
funkcji, funkcja forEach nie zwraca nic (undefined
). Na przykład:Podobnie jak
map
funkcja,forEach
wywołanie zwrotne podaje numer indeksu bieżącej iteracji jako drugi argument. Czy trzeci argument podaje tablicę, na którejforEach
została wywołana?Pętla za pomocą elementów
for..of
for..of
Pętli przechodzi poprzez każdy element tablicy (albo jakiejkolwiek innej iterowalny obiektu). Działa w następujący sposób:W powyższym przykładzie
element
oznacza element tablicy iarr
jest tablicą, którą chcemy zapętlić. Pamiętaj, że nazwaelement
jest dowolna i mogliśmy wybrać dowolną inną nazwę, np. „El” lub coś bardziej deklaratywnego, jeśli ma to zastosowanie.Nie myl
for..in
pętli zfor..of
pętlą.for..in
przejdzie przez wszystkie wyliczalne właściwości tablicy, podczas gdyfor..of
pętla będzie przechodzić tylko przez elementy tablicy. Na przykład:źródło