For-each nad poľom v JavaScripte?

Ako môžem prechádzať všetky položky v poli pomocou jazyka JavaScript?

Myslel som, že je to niečo také:

forEach(instance in theArray)

Kde theArray je moje pole, ale zdá sa, že to nie je správne.

Riešenie

TL;DR

  • Nepoužívajte for-in, pokiaľ ho nepoužívate s ochrannými opatreniami alebo si aspoň nie ste vedomí, prečo by vás to mohlo štípať.
  • Vaše najlepšie stávky sú zvyčajne
  • cyklus for-of (len ES2015+),
  • Array#forEach ([spec][1] | [MDN][2]) (alebo jeho príbuzné some a podobne) (iba ES5+),
  • jednoduchý staromódny cyklus for,
  • alebo for-in s ochrannými prvkami. Ale je toho oveľa viac, čo môžete preskúmať, čítajte ďalej...

    JavaScript má výkonnú sémantiku pre cykly cez polia a objekty podobné poliam. Odpoveď som'rozdelil na dve časti: Možnosti pre skutočné polia a možnosti pre veci, ktoré sú len poliampodobné, ako napríklad objekt argumenty, iné iterovateľné objekty (ES2015+), kolekcie DOM atď. Rýchlo poznamenám, že možnosti ES2015 môžete používať už teraz, dokonca aj na motoroch ES5, a to transpiláciou* ES2015 do ES5. Vyhľadajte "ES2015 transpiling" / "ES6 transpiling" pre viac informácií... Dobre, pozrime sa na naše možnosti:

    Pre skutočné polia

    Máte tri možnosti v [ECMAScript 5][3] ("ES5"), v súčasnosti najširšie podporovanej verzii, a ďalšie dve pridané v [ECMAScript 2015][4] ("ES2015", "ES6"):

  1. Používanie forEach a súvisiacich (ES5+)
  2. Použite jednoduchý cyklus for
  3. Používajte for-in správne
  4. Použite for-of (implicitne použite iterátor) (ES2015+)
  5. Explicitné použitie iterátora (ES2015+) Podrobnosti:

    1. Používajte forEach a súvisiace

    V každom nejasne modernom prostredí (teda nie v IE8), kde máte prístup k funkciám Array pridaným v ES5 (priamo alebo pomocou polyfillov), môžete použiť forEach ([spec][1] | [MDN][2]): -- begin snippet: js hide: false console: true babel: false -->

var a = ["a", "b", "c"];
a.forEach(function(entry) {
    console.log(entry);
});

forEach akceptuje funkciu spätného volania a voliteľne hodnotu, ktorá sa použije ako this pri volaní tohto spätného volania (vyššie nepoužité). Spätné volanie sa zavolá pre každú položku v poli v poradí, pričom sa preskočia neexistujúce položky v riedkych poliach. Hoci som vyššie použil len jeden argument, spätné volanie sa volá s tromi: Hodnota každej položky, index tejto položky a odkaz na pole, ktoré iterujete (v prípade, že ho vaša funkcia ešte nemá po ruke). Pokiaľ'nepodporujete zastarané prehliadače, ako je IE8 (ktorého trhový podiel podľa údajov spoločnosti NetApps v čase písania tohto článku v septembri 2016 bol len niečo vyše 4 %), môžete forEach spokojne používať na univerzálnej webovej stránke bez shimu. Ak potrebujete podporovať zastarané prehliadače, shimming/polyfilling forEach sa dá ľahko vykonať (vyhľadajte "es5 shim" pre niekoľko možností). forEach má tú výhodu, že nemusíte deklarovať indexovacie a hodnotové premenné v obsahujúcom obore, pretože sú dodávané ako argumenty iteračnej funkcie, a tak sú pekne zakreslené len na túto iteráciu. Ak sa obávate nákladov na vykonávanie funkcie pre každú položku poľa, nemusíte; podrobnosti. Okrem toho je forEach funkcia "loop through them all", ale ES5 definovala niekoľko ďalších užitočných "work your way through the array and do things" funkcií, vrátane:

  • [every][5] (zastaví cyklus pri prvom vrátení false alebo niečoho chybného)
  • [some][6] (zastaví cyklus, keď spätné volanie vráti prvýkrát true alebo niečo pravdivé)
  • [filter][7] (vytvorí nové pole obsahujúce prvky, pri ktorých funkcia filter vráti pravdivé a vynechá tie, pri ktorých vráti nepravdivé)
  • [map][8] (vytvorí nové pole z hodnôt vrátených spätným volaním)
  • [reduce][9] (vytvorí hodnotu opakovaným volaním spätného volania, pričom odovzdá predchádzajúce hodnoty; podrobnosti nájdete v špecifikácii; užitočné na sčítanie obsahu poľa a mnoho ďalších vecí)
  • [reduceRight][10] (ako reduce, ale pracuje v zostupnom, nie vzostupnom poradí)

    2. Použite jednoduchý cyklus for

    Niekedy sú staré spôsoby najlepšie:

var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
    console.log(a[index]);
}

Ak sa dĺžka poľa nebude počas cyklu meniť a je to v kóde citlivom na výkon (nepravdepodobné), trochu komplikovanejšia verzia, ktorá chytá dĺžku vopred, by mohla byť tiny trochu rýchlejšia:

var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
    console.log(a[index]);
}

A/alebo spätné počítanie:

var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
    console.log(a[index]);
}

V moderných JavaScriptových enginoch však len zriedkakedy potrebujete vyžmýkať posledný kúsok šťavy. V ES2015 a vyšších verziách môžete premenné index a hodnota urobiť lokálnymi v cykle for:

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 keď to urobíte, nielen value, ale aj index sa znovu vytvorí pre každú iteráciu cyklu, čo znamená, že uzávery vytvorené v tele cyklu si zachovajú odkaz na index (a value) vytvorený pre danú iteráciu:

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);
    });
}
<div>zero</div>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>

Ak by ste mali päť divov, dostali by ste "Index is: 0" ak by ste klikli na prvý a "Index je: 4" ak by ste klikli na posledný. Toto nefunguje, ak použijete var namiesto let.

3. Používajte for-in správne

Ľudia vám budú hovoriť, aby ste používali for-in, ale [na to for-in neslúži][11]. for-in prechádza cez vypočítateľné vlastnosti objektu, nie cez indexy poľa. Poradie nie je zaručené, dokonca ani v ES2015 (ES6). ES2015+ síce definuje poradie vlastností objektu (prostredníctvom [[OwnPropertyKeys]], [[Enumerate]] a vecí, ktoré ich používajú, ako napríklad [Object.getOwnPropertyKeys][12]), ale nedefinuje, že for-in bude toto poradie dodržiavať. (Podrobnosti v [tejto inej odpovedi][13].) Jediné skutočné prípady použitia for-in na poli sú:

  • Je to [sparse pole][14] s masívnymi medzerami v ňom, alebo
  • Používate neelementové vlastnosti a chcete ich zahrnúť do cyklu Ak sa pozrieme len na tento prvý príklad: Ak použijete vhodné ochranné opatrenia, môžete použiť for-in na navštívenie týchto voľných prvkov poľa:

// `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 
Komentáre (24)

Poznámka: Táto odpoveď je beznádejne zastaraná. Modernejší prístup nájdete na stránke metódy dostupné na poli. Zaujímavé metódy by mohli byť:

  • forEach
  • map
  • filter
  • zip
  • redukovať
  • každý
  • niektoré

Štandardný spôsob iterácie poľa v JavaScripte je jednoduchá slučka for:

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

Všimnite si však, že tento prístup je dobrý len vtedy, ak máte husté pole a každý index je obsadený prvkom. Ak je pole riedke, potom môžete pri tomto prístupe naraziť na výkonnostné problémy, pretože budete iterovať cez množstvo indexov, ktoré v poli skutočne neexistujú. V takom prípade môže byť lepším nápadom slučka for .. in. **Musíte však použiť vhodné ochranné opatrenia, aby ste zabezpečili, že sa bude konať len s požadovanými vlastnosťami poľa (t. j. s prvkami poľa), pretože slučka for ...in bude v starších prehliadačoch aj enumerovaná, alebo ak sú dodatočné vlastnosti definované ako enumerovateľné.

V ECMAScripte 5 bude na prototype poľa metóda forEach, ktorá však nie je podporovaná v starších prehliadačoch. Aby ste ju teda mohli dôsledne používať, musíte mať buď prostredie, ktoré ju podporuje (napríklad Node.js pre JavaScript na strane servera), alebo použiť "Polyfill". Polyfill pre túto funkcionalitu je však triviálny a keďže uľahčuje čítanie kódu, je dobré ho zahrnúť.

Komentáre (4)

Ak chcete vytvoriť cyklus nad poľom, použite štandardný trojdielny cyklus for.

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

Niektoré optimalizácie výkonu môžete dosiahnuť ukladaním myArray.length do vyrovnávacej pamäte alebo spätným iterovaním.

Komentáre (5)