For-each over een array in JavaScript?

Hoe kan ik met JavaScript door alle items in een array lopen?

Ik dacht dat het zoiets was als dit:

forEach(instance in theArray)

Waarbij deArray mijn array is, maar dit lijkt niet te kloppen.

Oplossing

TL;DR

  • Don't use for-in unless you use it with safeguards or are at least aware of why it might bite you.
  • Uw beste weddenschappen zijn meestal
  • een for-of lus (alleen ES2015+),
  • Array#forEach ([spec][1] | [MDN][2]) (of zijn verwanten some en dergelijke) (alleen ES5+),
  • een eenvoudige ouderwetse for lus,
  • of for-in met beveiligingen. Maar er is veel meer te ontdekken, lees verder...

    JavaScript heeft krachtige semantiek voor looping door arrays en array-achtige objecten. Ik'heb het antwoord in twee delen gesplitst: Opties voor echte arrays, en opties voor dingen die gewoon array-achtig zijn, zoals het arguments object, andere iterable objecten (ES2015+), DOM collecties, enzovoort. Ik'zal snel opmerken dat je de ES2015 opties nu kunt gebruiken, zelfs op ES5 engines, door transpiling van ES2015 naar ES5. Zoek naar "ES2015 transpiling" / "ES6 transpiling" voor meer... Oké, laten we eens kijken naar onze opties:

    Voor de eigenlijke Arrays

    Je hebt drie opties in [ECMAScript 5][3] ("ES5"), de versie die op dit moment het breedst ondersteund wordt, en nog twee die toegevoegd zijn in [ECMAScript 2015][4] ("ES2015", "ES6"):

  1. Gebruik forEach en aanverwanten (ES5+)
  2. Gebruik een eenvoudige for lus
  3. Gebruik for-in correct
  4. Gebruik for-of (gebruik impliciet een iterator) (ES2015+)
  5. Gebruik expliciet een iterator (ES2015+) Details:

    1. Gebruik forEach en aanverwante

    In elke vaag-moderne omgeving (dus niet IE8) waar je toegang hebt tot de Array functies die door ES5 zijn toegevoegd (direct of met behulp van polyfills), kun je forEach ([spec][1] | [MDN][2]) gebruiken:

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

forEach accepteert een callback functie en, optioneel, een waarde om te gebruiken als this bij het aanroepen van die callback (hierboven niet gebruikt). De callback wordt aangeroepen voor elke entry in de array, in volgorde, waarbij niet-bestaande entries in sparse arrays worden overgeslagen. Hoewel ik hierboven maar één argument gebruikte, wordt de callback aangeroepen met drie: De waarde van elke entry, de index van die entry, en een verwijzing naar de array waar je're iteratie over gaat (voor het geval je functie die niet al bij de hand heeft). Tenzij je'verouderde browsers zoals IE8 ondersteunt (die NetApps laat zien op iets meer dan 4% marktaandeel vanaf dit schrijven in september 2016), kun je gelukkig forEach gebruiken in een algemene webpagina zonder een shim. Als je toch verouderde browsers moet ondersteunen, is shimmen/polyfillen van forEach eenvoudig te doen (zoek naar "es5 shim" voor verschillende opties). forEach heeft het voordeel dat je geen index en waarde variabelen in de containing scope hoeft te declareren, omdat ze als argumenten aan de iteratie functie worden meegegeven, en dus mooi scoped naar alleen die iteratie. Als je je zorgen maakt over de runtime kosten van het maken van een functie-aanroep voor elke array entry, wees dat dan niet; details. Bovendien, forEach is de "loop through them all" functie, maar ES5 definieerde verschillende andere nuttige "work your way through the array and do things" functies, waaronder:

  • [every][5] (stopt de lus de eerste keer dat de callback false of iets vals teruggeeft)
  • [some][6] (stopt met de eerste keer dat de callback true of iets waarachtigs teruggeeft)
  • [filter][7] (creëert een nieuwe array met elementen waar de filter functie true retourneert en met weglating van de elementen waar het false retourneert)
  • [map][8] (creëert een nieuwe array van de waarden geretourneerd door de callback)
  • [reduce][9] (bouwt een waarde op door herhaaldelijk de callback aan te roepen, waarbij eerdere waarden worden doorgegeven; zie de spec voor de details; handig voor het optellen van de inhoud van een array en vele andere dingen)
  • [reduceRight][10] (zoals reduce, maar werkt in aflopende in plaats van oplopende volgorde)

    2. Gebruik een eenvoudige for lus

    Soms zijn de oude manieren de beste:

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

Als de lengte van de array niet zal veranderen tijdens de lus, en het is in performance-gevoelige code (onwaarschijnlijk), een iets gecompliceerdere versie die de lengte op voorhand grijpt zou een tiny beetje sneller kunnen zijn:

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

En/of terug tellen:

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

Maar met moderne JavaScript engines is het zeldzaam dat je dat laatste beetje sap moet uitpersen. In ES2015 en hoger, kun je je index en waarde variabelen lokaal maken voor de for loop:

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

En als je dat doet, wordt niet alleen value maar ook index opnieuw aangemaakt voor elke lus iteratie, wat betekent dat closures die in de lus body worden gemaakt een verwijzing houden naar de index (en value) die voor die specifieke iteratie is aangemaakt:

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>

Als je vijf divs had, zou je "Index is: 0" als je op de eerste klikte en "Index is: 4" als je op de laatste hebt geklikt. Dit werkt niet als u var gebruikt in plaats van let.

3. Gebruik for-in correct.

Je krijgt van mensen te horen dat je for-in moet gebruiken, maar [dat is niet waar for-in voor is][11]. for-in loopt door de numerable properties van een object, niet de indexen van een array. De volgorde is niet gegarandeerd, zelfs niet in ES2015 (ES6). ES2015+ definieert wel een volgorde voor objecteigenschappen (via [[OwnPropertyKeys]], [[Enumerate]], en dingen die ze gebruiken zoals [Object.getOwnPropertyKeys][12]), maar het definieert niet dat for-in die volgorde zal volgen. (Details in [dit andere antwoord][13].) De enige echte use-cases voor for-in op een array zijn: het is een [sparse arrays][14] met grote gaten erin, of U gebruikt niet-elementeigenschappen en die wilt u in de lus opnemen Als we alleen naar dat eerste voorbeeld kijken: Je kunt for-in gebruiken om die sparese array elementen te bezoeken als je de juiste voorzorgsmaatregelen gebruikt:


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

Noot: Dit antwoord is hopeloos verouderd. Voor een modernere aanpak, kijk naar de methoden die beschikbaar zijn op een array. Methoden van belang kunnen zijn:

  • forEach
  • map
  • filter
  • zip
  • verminderen
  • every
  • some

De standaard manier om een array te itereren in JavaScript is een vanilla for-lus:

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

Merk echter op dat deze aanpak alleen goed is als je een dichte array hebt, en elke index wordt bezet door een element. Als de array sparse is, dan kun je performance problemen krijgen met deze aanpak, omdat je over veel indices zult itereren die echt niet bestaan in de array. In dit geval is een for .. in-lus misschien een beter idee. Hoewel, je moet de juiste voorzorgsmaatregelen nemen om er zeker van te zijn dat alleen de gewenste eigenschappen van de array (dat wil zeggen, de array elementen) worden gebruikt, omdat de for..in-lus ook opgesomd zal worden in oudere browsers, of als de extra eigenschappen gedefinieerd zijn als enumerable.

In ECMAScript 5 komt er een forEach methode op het array prototype, maar deze wordt niet ondersteund in legacy browsers. Dus om het consistent te kunnen gebruiken moet je of een omgeving hebben die het ondersteunt (bijvoorbeeld Node.js voor server side JavaScript), of een "Polyfill" gebruiken. De Polyfill voor deze functionaliteit is echter triviaal en omdat het de code makkelijker leesbaar maakt, is het een goede polyfill om op te nemen.

Commentaren (4)

Als je een lus wilt maken over een array, gebruik dan de standaard driedelige for lus.

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

Je kunt de prestaties optimaliseren door myArray.length te cachen of er achterwaarts overheen te lopen.

Commentaren (5)