For-each per masyvą "JavaScript"?

Kaip, naudojant "JavaScript", pervesti ciklą per visus masyvo įrašus?

Maniau, kad tai yra kažkas panašaus:

forEach(instance in theArray)

kur theArray yra mano masyvas, bet atrodo, kad tai neteisinga.

Sprendimas

TL;DR

  • Nenaudokite for-in, jei nenaudojate jo su apsaugos priemonėmis arba bent jau nežinote, kodėl jis gali jums įgristi.
  • Geriausia, ką galite padaryti, paprastai yra
  • for-of ciklas (tik ES2015+),
  • Array#forEach ([spec][1] | [MDN][2]) (arba jo giminaičiai some ir panašūs) (tik ES5+),
  • paprastas senamadiškas for ciklas,
  • arba for-in su apsaugos priemonėmis. Tačiau yra dar daugybė dalykų, kuriuos galima ištirti, skaitykite toliau...

    "JavaScript" turi galingą ciklo per masyvus ir į masyvus panašius objektus semantiką. Atsakymą padalinau į dvi dalis: Tikrųjų masyvų parinktis ir į masyvus panašių dalykų, pavyzdžiui, objekto arguments, kitų iteruojamų objektų (ES2015+), DOM kolekcijų ir t. t., parinktis. Greitai pažymėsiu, kad ES2015 parinktis galite naudoti nuo šiol, net ES5 varikliuose, transpiliavę ES2015 į ES5. Ieškokite "ES2015 transpiling" / "ES6 transpiling", kad sužinotumėte daugiau... Gerai, apžvelkime savo galimybes:

    Faktiniams masyvams

    [ECMAScript 5][3] ("ES5"), šiuo metu plačiausiai palaikomoje versijoje, turite tris parinktis, o [ECMAScript 2015][4] ("ES2015", "ES6") pridėtos dar dvi ("ES2015", "ES6"):

  1. Naudokite forEach ir susijusius (ES5+)
  2. Naudokite paprastą for ciklą
  3. Naudokite for-in korektiškai
  4. Naudokite for-of (netiesiogiai naudokite iteratorių) (ES2015+)
  5. Aiškiai naudokite iteratorių (ES2015+) Išsami informacija:

    1. Naudokite forEach ir susijusius

    Bet kokioje neaiškios šiuolaikinės technologijos aplinkoje (taigi, ne IE8), kurioje turite prieigą prie ES5 pridėtų Array funkcijų (tiesiogiai arba naudodami polipinius papildymus), galite naudoti forEach ([spec][1] | [MDN][2]):

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

forEach priima grįžtamojo ryšio funkciją ir, pasirinktinai, reikšmę, kuri bus naudojama kaip this, kai bus kviečiamas šis grįžtamasis ryšys (pirmiau nenaudota). Grįžtamasis skambutis kviečiamas kiekvienam masyvo įrašui eilės tvarka, praleidžiant neegzistuojančius įrašus retuose masyvuose. Nors pirmiau naudojau tik vieną argumentą, grįžtamasis skambutis kviečiamas su trimis argumentais: kiekvieno įrašo vertė, to įrašo indeksas ir nuoroda į masyvą, kurį iteruojate (jei jūsų funkcija dar neturi jos po ranka). Jei nepalaikysite pasenusių naršyklių, tokių kaip IE8 (NetApps rodo, kad jos rinkos dalis šio rašinio rašymo metu, 2016 m. rugsėjį buvo vos daugiau nei 4 %), forEach bendrojo naudojimo tinklalapyje galite drąsiai naudoti be shim'o. Jei jums reikia palaikyti pasenusias naršykles, nesunkiai galima atlikti forEach shimming/polyfilling (ieškokite "es5 shim" kelių parinkčių). forEach privalumas yra tas, kad jums nereikia deklaruoti indeksavimo ir vertės kintamųjų turinčioje srityje, nes jie pateikiami kaip iteracijos funkcijos argumentai, todėl yra gražiai pritaikyti tik tai iteracijai. Jei nerimaujate, kad dėl kiekvieno masyvo įrašo teks atlikti funkcijos iškvietimą, nesijaudinkite; informacija. Be to, forEach yra "ciklo per juos visus" funkcija, tačiau ES5 apibrėžė keletą kitų naudingų "pereikite per masyvą ir atlikite veiksmus" funkcijų, pvz:

  • [every][5] (nutraukia ciklą, kai pirmą kartą grįžtamasis skambutis grąžina false arba ką nors klaidingo)
  • [`some][6] (sustabdo ciklą pirmą kartą, kai grįžtamasis skambutis grąžina true arba ką nors teisingo)
  • [filter][7] (sukuria naują masyvą, į kurį įtraukiami elementai, kai filtravimo funkcija grąžina teisingą, ir praleidžiami elementai, kai ji grąžina neteisingą)
  • [`map][8] (sukuria naują masyvą iš reikšmių, kurias grąžino grįžtamasis skambutis)
  • [reduce][9] (sukuria vertę pakartotinai skambindama atgaliniam skambučiui ir perduodama ankstesnes vertes; išsamią informaciją žr. specifikacijoje; naudinga sumuojant masyvo turinį ir daugeliui kitų dalykų)
  • [reduceRight][10] (kaip ir reduce, bet veikia mažėjimo, o ne didėjimo tvarka)

    2. Naudokite paprastą for ciklą

    Kartais seni būdai yra geriausi:

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

Jei ciklo metu masyvo ilgis nesikeis ir tai yra našumui jautrus kodas (mažai tikėtina), šiek tiek sudėtingesnis variantas, kai ilgis nustatomas iš anksto, gali būti šiek tiek greitesnis:

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

Ir/arba skaičiuojant atgal:

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

Tačiau su šiuolaikiniais JavaScript varikliais retai kada prireikia išgauti tą paskutinį sulčių kiekį. ES2015 ir naujesnėse versijose galite padaryti savo indekso ir reikšmės kintamuosius vietiniais for ciklo kintamaisiais:

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"
}

Kai tai darote, ne tik value, bet ir index iš naujo sukuriama kiekvienai ciklo iteracijai, t. y. ciklo kūne sukurtos uždaromosios struktūros išlaiko nuorodą į konkrečiai iteracijai sukurtą index (ir value):

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>

Jei turėtumėte penkis divus, gautumėte "Index is: 0", jei spustelėtumėte pirmąją ir "Index is: Jei spustelėjote paskutinįjį, būtų: &" 4". Tai neveikia, jei vietoj let naudojate var.

3. Naudokite for-in tinkamai

Jums' žmonės lieps naudoti for-in, bet for-in skirtas ne tam, kad būtų galima jį naudoti [11]. for-in sudaro ciklą per objekto išskaičiuojamas savybes, o ne per masyvo indeksus. Tokia tvarka negarantuojama net ES2015 (ES6). ES2015+ apibrėžia objektų savybių eiliškumą (per [[OwnPropertyKeys]], [[Enumerate]] ir juos naudojančius dalykus, pavyzdžiui, [Object.getOwnPropertyKeys][12]), tačiau neapibrėžia, kad for-in laikysis šio eiliškumo. (Išsamiau [šiame kitame atsakyme][13].) Vieninteliai realūs for-in naudojimo atvejai masyve yra šie:

  • Tai [plonas masyvas][14], kuriame yra dideli tarpai, arba
  • Naudojate neelementines savybes ir norite jas įtraukti į ciklą Žvelgiant tik į pirmąjį pavyzdį: Galite naudoti for-in, kad aplankytumėte tuos retus masyvo elementus, jei naudojate tinkamas apsaugos priemones:

// `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 
Komentarai (24)

Pastaba: Šis atsakymas yra beviltiškai pasenęs. Šiuolaikiškesnio požiūrio ieškokite metodai, kuriais galima naudotis masyvu. Įdomūs metodai gali būti šie:

  • forEach
  • žemėlapis
  • filtras
  • zip
  • sumažinti
  • kiekvienas
  • kai kurie

Standartinis būdas iteruoti masyvą JavaScript kalba yra "vanilinė" for kilpa:

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

Tačiau atkreipkite dėmesį, kad šis metodas tinka tik tada, jei masyvas yra tankus ir kiekvienas indeksas yra užimtas elemento. Jei masyvas yra retas, taikant šį metodą gali kilti našumo problemų, nes bus iteruojama per daug indeksų, kurių iš tikrųjų nėra masyve. Tokiu atveju geresnė idėja būtų for .. in ciklas. Tačiau turite naudoti atitinkamas apsaugos priemones, kad užtikrintumėte, jog bus veikiama tik su norimomis masyvo savybėmis (t. y. su masyvo elementais), nes for...in ciklas taip pat bus skaičiuojamas senesnėse naršyklėse arba jei papildomos savybės apibrėžtos kaip enumerable.

ECMAScript 5 bus sukurtas masyvo prototipo forEach metodas, tačiau senosiose naršyklėse jis nepalaikomas. Taigi, norėdami nuosekliai jį naudoti, turite turėti aplinką, kuri jį palaiko (pavyzdžiui, Node.js serverio pusės JavaScript), arba naudoti "Polyfill". Tačiau šios funkcijos polistilius yra trivialus, o kadangi jis palengvina kodo skaitymą, jį verta įtraukti.

Komentarai (4)

Jei norite cikliškai peržiūrėti masyvą, naudokite standartinį trijų dalių ciklą for.

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

Galite šiek tiek optimizuoti našumą spartindami myArray.length arba iteruodami per jį atgal.

Komentarai (5)