Dla każdego w tablicy w JavaScript


4682

Jak mogę przeglądać wszystkie wpisy w tablicy za pomocą JavaScript?

Myślałem, że to coś takiego:

forEach(instance in theArray)

Gdzie theArrayjest moja tablica, ale wydaje się to niepoprawne.


16
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.
Slaven Rezic

35
Array.ForEach jest około 95% wolniejszy niż for () dla każdego dla tablic w JavaScript. Zobacz ten test wydajności online: jsperf.com/fast-array-foreach via coderwall.com/p/kvzbpa
molokoloco

77
W wielu sytuacjach 95% wolniejszy nie będzie znaczący blog.niftysnippets.org/2012/02/foreach-and-runtime-cost.html
David Sykes

7
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”):

  1. Zastosowanie forEachi powiązane (ES5 +)
  2. Użyj prostej forpętli
  3. Używaj for-in poprawnie
  4. Użyj for-of(użyj pośrednio iteratora) (ES2015 +)
  5. 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]);
}

I / lub odliczanie wstecz:

var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --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"

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

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-in poprawnie

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ą:

  • To rzadkie tablice z ogromnymi lukami, lub
  • 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 array
var 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]);
    }
}

Zwróć uwagę na trzy kontrole:

  1. Że przedmiot ma swój własny obiekt o tej samej nazwie (nie jeden to dziedziczy od swojego prototypu) oraz

  2. Że kluczem są wszystkie cyfry dziesiętne (np. Normalna postać łańcucha, a nie notacja naukowa), i

  3. 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:

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-ofinstrukcja. To wygląda tak:

const a = ["a", "b", "c"];
for (const val of a) {
    console.log(val);
}

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:

  1. 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`
    });
  2. Użyj prostej forpętli

    Oczywiście prosta forpętla dotyczy obiektów podobnych do tablicy.

  3. Używaj for-in poprawnie

    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.

  4. 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.

  5. 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:

  1. 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.

  2. 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")];
  3. 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ć).


44
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.


2
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).
PatrikAkerstrand,

239

Jeśli korzystasz z biblioteki jQuery , możesz użyć jQuery.each :

$.each(yourArray, function(index, value) {
  // do your stuff here
});

EDYTOWAĆ :

Jak na pytanie, użytkownik chce kodu w javascript zamiast jquery, więc edycja jest

var length = yourArray.length;   
for (var i = 0; i < length; i++) {
  // Do something with yourArray[i].
}

2
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:

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++) { ... }   // Forwards

for (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 granicami undefined .

  • 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.


3
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):

arr.forEach(function (val, index, theArray) {
    //do stuff
});

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.


3
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.


6
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:

angular.forEach(temp, function(item) {
    temp2.push(item);
});

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 loop
for (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.


34

Jeśli nie masz nic przeciwko opróżnieniu tablicy:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

xbędzie zawierać ostatnią wartość yi zostanie usunięty z tablicy. Możesz także użyć, shift()który da i usunie pierwszy element y.


4
To nie działa, jeśli masz rzadką tablicę, taką jak [1, 2, undefined, 3].
M. Grzywaczewski

2
... lub cokolwiek innego falsey: [1, 2, 0, 3]lub[true, true, false, true]
joeytwiddle

31

ForEach realizacja ( patrz w jsFiddle ):

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

2
iterator w tym robi niepotrzebne obliczenia długości. W idealnym przypadku długość listy należy obliczyć tylko raz.
MIdhun Krishna,

2
@MIdhunKrishna Zaktualizowałem moją odpowiedź i jsFiddle, ale pamiętaj, że nie jest to tak proste, jak myślisz. Sprawdź to pytanie
nmoliveira

2
Pełną i prawidłową implementację można znaleźć tutaj: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
marciowb

29

Prawdopodobnie for(i = 0; i < array.length; i++)pętla nie jest najlepszym wyborem. Dlaczego? Jeśli masz to:

var array = new Array();
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;
        }
    }
}

Przykład:

foreach(array, function(el){
    if(el != "!"){
        console.log(el);
    } else {
        console.log(el+"!!");
    }
});

Zwraca:

//Hello
//World
//!!!

29

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

2
Dziennik konsoli służy tylko do demonstracji. To sprawia, że ​​jest to działający przykład cmplete.
Rajesh Paul

29

Od ECMAScript 6:

list = [0, 1, 2, 3]
for (let obj of list) {
    console.log(obj)
}

Gdzie ofunika osobliwości związanych z ini sprawia, że ​​działa jak forpętla dowolnego innego języka i letwiąż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).


28

Ł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.

Przykładem działania CodePen jest:

var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});

Zobacz też


23

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.

for (var instance in objects) {
   ...
}

Należy jednak pamiętać, że mogą istnieć powody, aby używać jeszcze prostszej forpętli (patrz pytanie Przepełnienie stosu Dlaczego używanie „dla… w” z iteracją tablicy jest tak złym pomysłem? )

var instance;
for (var i=0; i < objects.length; i++) {
    var instance = objects[i];
    ...
}

22

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

    return this;
}

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]

Przykład nr 2

each.call(document.getElementsByTagName('p'), "this.className = data;",'blue');

Każdy tag p dostaje się class="blue"

Przykład nr 3

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.


21

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 id="example"></p>

while - pętla, gdy warunek się skończy. To wydaje się być najszybszą pętlą

var text = "";
var i = 0;
while (i < 10) {
    text +=  i + ") something<br>";
    i++;
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

do / while - również pętla przez blok kodu, gdy warunek jest spełniony, uruchomi się przynajmniej raz

var text = ""
var i = 0;

do {
    text += i + ") something <br>";
    i++;
}
while (i < 10);

document.getElementById("example").innerHTML = text;
<p id="example"></p>

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 function
var numbers = [65, 44, 12, 4];
document.getElementById("example").innerHTML = numbers.map(function(num){return num * 2});
<p id="example"></p>

Aby 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 .


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.

[1,2,3,4].reduce(function(previous, current) {
  return previous + current;
});
// Output: 10
// 1st iteration: previous=1, current=2 => result=3
// 2nd iteration: previous=3, current=3 => result=6
// 3rd iteration: previous=6, current=4 => result=10

every - Zwraca true lub false, jeśli wszystkie elementy w tablicy przejdą test w funkcji zwrotnej.

// Check if everybody has 18 years old of more.
var ages = [30, 43, 18, 5];
ages.every(function(elem) {
  return elem >= 18;
});

// Output: false

filter - bardzo podobny do każdego z wyjątkiem tego, że filter zwraca tablicę z elementami, które zwracają wartość true dla danej funkcji.

// Finding the even numbers
[1,2,3,4,5,6].filter(function(elem){
  return (elem % 2 == 0)
});

// Output: [2,4,6]

16

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


13

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.


12

jQuery sposób za pomocą $.map:

var data = [1, 2, 3, 4, 5, 6, 7];

var newData = $.map(data, function(element) {
    if (element % 2 == 0) {
        return element;
    }
});

// newData = [2, 4, 6];

1
Myślę, że wyjście będzie bardziej prawdopodobne [undefined, 2, undefined, 4, undefined, 6, undefined].

10

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.

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

Przykład 2: Podziel słowa na znaki

let arrFruits = ['apple', 'orange', 'banana'];

for (let [firstLetter, ...restOfTheWord] of arrFruits) {
  // Create a shallow copy using the spread operator
  let [lastLetter] = [...restOfTheWord].reverse();
  console.log(firstLetter, lastLetter, restOfTheWord);
}

Przykład 3: Pętla za pomocą keya ivalue

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

Przykład 4: Pobierz właściwości obiektu

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];

for (let { name, age: aliasForAge } of arrWithObjects) {
  console.log(name, aliasForAge);
}

Przykład 5: Uzyskaj właściwości głębokiego obiektu tego, czego potrzebujesz

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

for (let { name, tags: [firstItemFromTags, ...restOfTags] } of arrWithObjectsWithArr) {
  console.log(name, firstItemFromTags, restOfTags);
}

Przykład 6: Czy użyto przykładu 3 z.forEach

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

Przykład 7: Czy użyto przykładu 4 z.forEach

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

Przykład 8: Czy w przykładzie 5 użyto.forEach

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

arrWithObjectsWithArr.forEach(({
  name,
  tags: [firstItemFromTags, ...restOfTags]
}) => {
  console.log(name, firstItemFromTags, restOfTags);
});


7

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:

var myArray = [1, 2, 3];
myArray = myArray.map(
  (item) => {
    return item + 1;
  }
);

console.log(myArray); // [2, 3, 4]

1
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:

1
3
2

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
}

6

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');
})


6

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

5

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

Możesz również przekształcić swoją zwykłą tablicę w iterator, wykonując Symbol.iteratornastępujące czynności:

const myArr = [2,3,4]

let it = myArr[Symbol.iterator]();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

Możesz również przekształcić swojego zwykłego arrayw iteratortaki:

let myArr = [8, 10, 12];

function makeIterator(array) {
    var nextIndex = 0;
    
    return {
       next: function() {
           return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    };
};

var it = makeIterator(myArr);

console.log(it.next().value);   // {value: 8, done: false}
console.log(it.next().value);   // {value: 10, done: false}
console.log(it.next().value);   // {value: 12, done: false}
console.log(it.next().value);   // {value: undefined, done: true}

UWAGA :

  • Iteratory mają charakter wyczerpujący.
  • Obiekty nie są iterabledomyślnie. Użyj for..inw takim przypadku, ponieważ zamiast wartości działa z kluczami.

Możesz przeczytać więcej o iteration protocol tutaj .


5

Jeśli chcesz użyć forEach(), będzie wyglądać jak:

theArray.forEach ( element => {
    console.log(element);
});

Jeśli chcesz użyć for(), będzie wyglądać jak:

for(let idx = 0; idx < theArray.length; idx++){
    let element = theArray[idx];
    console.log(element);
}

4

Zgodnie z nową zaktualizowaną funkcją ECMAScript 6 (ES6) i ECMAScript 2015 można używać następujących opcji z pętlami:

dla pętli

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

// Output: 0,1,2,3,4

dla ... w pętlach

let obj = {"a":1, "b":2}

for(let k in obj){
  console.log(k)
}

// Output: a,b

Array.forEach ()

let array = [1,2,3,4]

array.forEach((x) => {
  console.log(x);
})

// Output: 1,2,3,4

dla ... pętli

let array = [1,2,3,4]

for(let x of array){
  console.log(x);
}

// Output: 1,2,3,4

podczas pętli

let x = 0

while(x < 5){
  console.log(x)
  x++
}

// Output: 1,2,3,4

zrobić ... podczas pętli

let x = 0

do{
  console.log(x)
  x++
}while(x < 5)

// Output: 1,2,3,4

4

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 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:

  • A - fortesty
  • B - whiletesty
  • C - inne / alternatywne metody

Wyniki dla różnych przeglądarek

Wyniki dla wszystkich testowanych przeglądarek

Wpisz opis zdjęcia tutajprzeglądarki **

Tablica z 10 elementami

Wyniki dla Chrome. Możesz wykonać test na swoim komputerze tutaj .

Wpisz opis zdjęcia tutaj

Tablica z 1 000 000 elementów

Wyniki dla Chrome. Możesz wykonać test na swoim komputerze tutaj

Wpisz opis zdjęcia tutaj


4

Podsumowanie:

Podczas iteracji po tablicy często chcemy osiągnąć jeden z następujących celów:

  1. Chcemy iterować tablicę i utworzyć nową tablicę:

    Array.prototype.map

  2. Chcemy iterować tablicę i nie tworzyć nowej tablicy:

    Array.prototype.forEach

    for..of pę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);
}

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.