Vraag Voor-elk over een array in JavaScript?


Hoe kan ik alle items in een array doorlopen met behulp van JavaScript?

Ik dacht dat het zoiets was als dit:

forEach(instance in theArray)

Waar theArray is mijn array, maar dit lijkt onjuist te zijn.


3782
2018-02-17 13:51


oorsprong


antwoorden:


TL; DR

  • Niet gebruiken for-in tenzij je het gebruikt met voorzorgsmaatregelen of je er op zijn minst van bewust bent waarom het je zou kunnen bijten.
  • Uw beste weddenschappen zijn meestal

    • een for-of loop (alleen ES2015 +),
    • Array#forEach (spec | MDN) (of zijn familieleden some en dergelijke) (alleen ES5 +),
    • een eenvoudige ouderwetse for lus,
    • of for-in met beveiligingen.

Maar er is veel meer om te ontdekken, lees verder ...


JavaScript heeft een krachtige semantiek voor het doorlopen van arrays en arrayachtige objecten. Ik heb het antwoord opgesplitst in twee delen: opties voor echte arrays en opties voor dingen die slechts een array zijngraag willen, zoals de arguments object, andere iterabele objecten (ES2015 +), DOM-collecties, enzovoort.

Ik zal snel opmerken dat je de ES2015-opties kunt gebruiken nu, zelfs op ES5-motoren, door transpiling ES2015 tot ES5. Zoeken naar "ES2015 transpiling" / "ES6 transpiling" voor meer ...

Oké, laten we naar onze opties kijken:

Voor werkelijke reeksen

Je hebt drie opties in ECMAScript 5 ("ES5"), de versie die momenteel het meest wordt ondersteund, en er nog twee aan toegevoegd ECMAScript 2015 ("ES2015", "ES6"):

  1. Gebruik forEach en gerelateerd (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 gerelateerd

In elke vaag-moderne omgeving (dus niet IE8) waar je toegang hebt tot de Array functies toegevoegd door ES5 (rechtstreeks of met behulp van polyfills), die u kunt gebruiken forEach (spec | MDN):

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 opgeroepen voor elke vermelding in de array, om niet-bestaande items in maze arrays over te slaan. Hoewel ik hierboven slechts één argument heb gebruikt, wordt de callback met drie aangeroepen: de waarde van elk item, de index van dat item en een verwijzing naar de array waarover u itereert (voor het geval uw functie het al niet bij de hand heeft) ).

Tenzij u verouderde browsers zoals IE8 (die NetApps met iets meer dan 4% marktaandeel toont vanaf dit schrijven in september 2016) ondersteunt, kunt u deze graag gebruiken forEach in een webpagina voor algemene doeleinden zonder een schijf. Als u verouderde browsers moet ondersteunen, shimming / polyfilling forEach is gemakkelijk gedaan (zoek naar "es5 shim" voor verschillende opties).

forEach heeft het voordeel dat u indexerings- en waarde-variabelen niet hoeft te declareren in het insluitende bereik, omdat ze worden geleverd als argumenten voor de iteratiefunctie en dus net zo goed zijn afgestemd op alleen die iteratie.

Als u zich zorgen maakt over de runtime-kosten voor het maken van een functieaanroep voor elk arrayitem, wees dat dan niet; gegevens.

Bovendien, forEach is de "doorloop ze allemaal" -functie, maar ES5 definieerde verschillende andere nuttige "werk je weg door de array en doe dingen" -functies, waaronder:

  • every (stopt met lus de eerste keer dat de callback terugkeert false of iets falsey)
  • some (stopt met lus de eerste keer dat de callback terugkeert true of iets waars)
  • filter (maakt een nieuwe array inclusief elementen waar de filterfunctie terugkeert true en het weglaten van degene waar het terugkeert false)
  • map (maakt een nieuwe array aan op basis van de waarden die door de callback worden geretourneerd)
  • reduce (bouwt een waarde op door herhaaldelijk de callback te bellen, eerdere waarden door te geven, zie de specificatie voor de details; handig voor het optellen van de inhoud van een array en vele andere zaken)
  • reduceRight (graag willen reduce, maar werkt in aflopende volgorde in plaats van in 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 verandert tijdens de lus, en het is in een prestatie-gevoelige code (onwaarschijnlijk), kan een iets gecompliceerdere versie die de lengte naar voren grijpt een klein een beetje sneller:

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

En / of achteruit 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 eruit haalt.

In ES2015 en hoger kunt u uw index- en waarde-variabelen lokaal maken in de for lus:

let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
    let value = a[index];
}
//console.log(index); // Would cause "ReferenceError: index is not defined"
//console.log(value); // Would cause "ReferenceError: value is not defined"

En als je dat doet, niet alleen value maar ook index wordt opnieuw gemaakt voor elke lus-iteratie, wat betekent dat sluitingen die in het luslichaam zijn gemaakt, een verwijzing naar de index (en value) gemaakt voor die specifieke iteratie:

let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
    divs[index].addEventListener('click', e => {
        alert("Index is: " + index);
    });
}

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

3. Gebruik for-in  correct

Je krijgt mensen te horen die je moet gebruiken for-in, maar dat is niet wat for-in is voor. for-in loopt door de opsombare eigenschappen van een object, niet de indexen van een array. De bestelling kan niet worden gegarandeerd, zelfs niet in ES2015 (ES6). ES2015 definieert een opdracht om eigenschappen te objectiveren (via [[OwnPropertyKeys]], [[Enumerate]]en dingen die ze gebruiken Object.getOwnPropertyKeys), maar het doet niet definieer dat for-in zal die volgorde volgen. (Details in dit andere antwoord.)

Toch, het kan nuttig zijn, vooral voor dun arrays, als u 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 are explained
        /^0$|^[1-9]\d*$/.test(key) &&    // and then hidden
        key <= 4294967294                // away below
        ) {
        console.log(a[key]);
    }
}

Let op de twee controles:

  1. Dat het object het heeft eigen eigenschap met die naam (niet een ervan ervaart het van zijn prototype), en

  2. Dat de sleutel een numerieke string van basis-10 is in de normale stringvorm en dat de waarde <= 2 ^ 32 - 2 is (wat 4.294.967.294 is). Waar komt dat nummer vandaan? Het maakt deel uit van de definitie van een array-index in de specificatie. Andere nummers (niet-gehele getallen, negatieve getallen, getallen groter dan 2 ^ 32 - 2) zijn geen array-indexen. De reden dat het 2 ^ 32 is - 2 is dat dat de grootste indexwaarde één lager maakt dan 2 ^ 32 - 1, wat de maximale waarde van een array is length kan hebben. (Bijvoorbeeld, de lengte van een array past in een 32-bits geheel getal zonder teken.) (Props to RobG for wijzend in een opmerking op mijn blogpost dat mijn vorige test niet helemaal goed was.)

Dat is een klein beetje toegevoegde overhead per lus-iteratie op de meeste arrays, maar als je een hebt dun array, het kan een efficiëntere manier zijn om te lusen omdat het alleen lust voor items die feitelijk bestaan. Bijvoorbeeld, voor de bovenstaande array lussen we in totaal drie keer (voor toetsen "0", "10", en "10000"- onthoud, het zijn snaren), niet 10.001 keer.

Nu wil je dat niet elke keer schrijven, dus je kunt dit in je toolkit plaatsen:

function arrayHasOwnIndex(array, prop) {
    return array.hasOwnProperty(prop) && /^0$|^[1-9]\d*$/.test(prop) && prop <= 4294967294; // 2^32 - 2
}

En dan zouden we het op deze manier gebruiken:

for (key in a) {
    if (arrayHasOwnIndex(a, key)) {
        console.log(a[key]);
    }
}

Of als u alleen geïnteresseerd bent in een "goed genoeg voor de meeste gevallen" -test, kunt u dit gebruiken, maar terwijl het in de buurt is, klopt het niet helemaal:

for (key in a) {
    // "Good enough" for most cases
    if (String(parseInt(key, 10)) === key && a.hasOwnProperty(key)) {
        console.log(a[key]);
    }
}

4. Gebruik for-of (gebruik impliciet een iterator) (ES2015 +)

ES2015 voegt toe iterators naar JavaScript. De eenvoudigste manier om iterators te gebruiken is de nieuwe for-of uitspraak. Het ziet er zo uit:

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

Output:

een
b
c

Onder de dekens krijgt dat een iterator van de array en loopt er doorheen en haalt de waarden eruit. Dit heeft niet het probleem dat gebruiken for-in heeft, omdat het een iterator gebruikt die wordt gedefinieerd door het object (de array), en arrays definiëren dat hun iterators doorheen hun entries (niet hun eigenschappen). anders for-in in ES5 is de volgorde waarin de vermeldingen worden bezocht de numerieke volgorde van hun indexen.

5. Gebruik expliciet een iterator (ES2015 +)

Soms wilt u misschien een iterator gebruiken uitdrukkelijk. U kunt dat ook doen, hoewel het een stuk onhandiger is dan for-of. Het ziet er zo uit:

var a = ["a", "b", "c"];
var it = a.values();
var entry;
while (!(entry = it.next()).done) {
    console.log(entry.value);
}

De iterator is een object dat overeenkomt met de Iterator-definitie in de specificatie. Haar next methode geeft een nieuw antwoord resultaat object elke keer dat je het noemt. Het resultaatobject heeft een eigenschap, done, ons te vertellen of het klaar is, en een eigendom value met de waarde voor die iteratie. (done is optioneel als het zou zijn false, value is optioneel als het zou zijn undefined.)

De betekenis van value varieert afhankelijk van de iterator; arrays ondersteunen (ten minste) drie functies die iterators retourneren:

  • values(): Dit is degene die ik hierboven heb gebruikt. Het retourneert een iterator waar elk value is de array-invoer voor die iteratie ("a", "b", en "c" in het voorbeeld eerder).
  • keys(): Retourneert een iterator waar elk value is de sleutel voor die iteratie (dus voor onze a hierboven zou dat zijn "0", dan "1", dan "2").
  • entries(): Retourneert een iterator waar elk value is een array in de vorm [key, value] voor die iteratie.

Voor array-achtige objecten

Afgezien van echte arrays, zijn er ook batterijachtige objecten met een length eigendom en eigenschappen met numerieke namen: NodeList instanties, de arguments object, etc. Hoe doorlopen we hun inhoud?

Gebruik een van de bovenstaande opties voor arrays

Ten minste enkele en mogelijk de meeste of zelfs alle van de arraybenaderingen hierboven zijn vaak even goed van toepassing op arrayachtige objecten:

  1. Gebruik forEach en gerelateerd (ES5 +)

    De verschillende functies aan Array.prototype zijn "opzettelijk generiek" en kunnen meestal via array-achtige objecten worden gebruikt Function#call of Function#apply. (Zie de Waarschuwing voor door de host geleverde objecten aan het einde van dit antwoord, maar het is een zeldzaam probleem.)

    Stel dat je het wilde gebruiken forEach op een Node's childNodes eigendom. Je zou dit doen:

    Array.prototype.forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });
    

    Als je dat veel gaat doen, wil je misschien een kopie van de functie-verwijzing pakken in een variabele voor hergebruik, bijvoorbeeld:

    // (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. Gebruik een eenvoudige for lus

    Uiteraard een simpele for lus is van toepassing op arrayachtige objecten.

  3. Gebruik for-in  correct

    for-in met dezelfde beveiligingen als bij een array zou het ook moeten werken met array-achtige objecten; de waarschuwing voor door de gastheer verstrekte objecten op # 1 hierboven is mogelijk van toepassing.

  4. Gebruik for-of (gebruik impliciet een iterator) (ES2015 +)

    for-of gebruikt de iterator van het object (indien aanwezig); we zullen moeten zien hoe dit speelt met de verschillende array-achtige objecten, in het bijzonder de door host geleverde objecten. Bijvoorbeeld de specificatie voor de NodeList van querySelectorAll is bijgewerkt om iteratie te ondersteunen. De specificatie voor de HTMLCollection van getElementsByTagName was niet.

  5. Gebruik expliciet een iterator (ES2015 +)

    Zie # 4, we zullen moeten zien hoe iterators spelen.

Maak een echte array

Soms wilt u misschien een arrayachtig object omzetten in een echte array. Dat doen is verrassend eenvoudig:

  1. Gebruik de slice methode van matrices

    We kunnen de gebruiken slice methode van arrays, die net als de andere hierboven genoemde methoden "intentioneel generiek" is en dus kan worden gebruikt met array-achtige objecten, zoals deze:

    var trueArray = Array.prototype.slice.call(arrayLikeObject);
    

    Dus bijvoorbeeld als we een willen converteren NodeList in een ware array, kunnen we dit doen:

    var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
    

    Zie de Waarschuwing voor door de host geleverde objecten hieronder. Houd er rekening mee dat dit in IE8 en eerder zal mislukken, waardoor u geen door host geleverde objecten als gebruiken this zoals dat.

  2. Gebruik spread syntaxis (...)

    Het is ook mogelijk om ES2015's te gebruiken spreid syntaxis uit met JavaScript-engines die deze functie ondersteunen:

    var trueArray = [...iterableObject];
    

    Dus bijvoorbeeld als we een willen converteren NodeList in een ware array, met spreidingssyntaxis wordt dit vrij kort:

    var divs = [...document.querySelectorAll("div")];
    
  3. Gebruik Array.from  (Spec) | (MDN)

    Array.from (ES2015 +, maar eenvoudig polyfilled) maakt een array van een array-achtig object, waarbij optioneel de invoer eerst door een toewijzingsfunctie wordt geleid. Zo:

    var divs = Array.from(document.querySelectorAll("div"));
    

    Of als u een array van de tagnamen van de elementen met een bepaalde klasse wilt ophalen, gebruikt u de toewijzingsfunctie:

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

Waarschuwing voor door de host geleverde objecten

Als je gebruikt Array.prototype functies met gastheer geleverde array-achtige objecten (DOM-lijsten en andere dingen die de browser biedt in plaats van de JavaScript-engine), moet u zeker zijn dat u in uw doelomgevingen test om te zorgen dat het door de host geleverde object zich correct gedraagt. De meesten gedragen zich goed (nu), maar het is belangrijk om te testen. De reden is dat de meeste van de Array.prototype methoden die u waarschijnlijk wilt gebruiken, vertrouwen op het door de host geleverde object en geven een eerlijk antwoord op het abstract [[HasProperty]] operatie. Vanaf dit schrijven doen browsers dit uitstekend, maar de 5.1-specificatie maakte wel de mogelijkheid mogelijk dat een door een host geleverd object niet eerlijk is. Het is in §8.6.2, enkele paragrafen onder de grote tafel aan het begin van die sectie), waar staat:

Host-objecten kunnen deze interne methoden op elke manier implementeren, tenzij anders aangegeven; bijvoorbeeld, een mogelijkheid is dat [[Get]] en [[Put]] voor een bepaald hostobject inderdaad eigenschappen ophalen maar opslaan [[HasProperty]] genereert altijd vals.

(Ik kon de equivalente woordenstroom in de ES2015-specificatie niet vinden, maar het is nog steeds het geval.) Nogmaals, vanaf dit moment hebben de door de gemeenschappelijke host geleverde array-achtige objecten in moderne browsers [NodeList voorbeelden, bijvoorbeeld] do handvat [[HasProperty]] correct, maar het is belangrijk om te testen.)


5976
2018-02-17 13:53



Bewerk: Dit antwoord is hopeloos verouderd. Voor een meer moderne aanpak, kijk naar de methoden beschikbaar op een array. Methoden van belang kunnen zijn:

  • forEach
  • kaart
  • filter
  • ritssluiting
  • verminderen
  • elk
  • sommige

De standaardmanier om een ​​array in te herhalen JavaScript is een vanille for-lus:

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

Merk echter op dat deze benadering alleen goed is als je een dichte array hebt en elke index wordt bezet door een element. Als de array schaars is, kunt u met deze aanpak prestatieproblemen tegenkomen, omdat u veel indices opnieuw zult gebruiken die niet werkelijk bestaan ​​in de array. In dit geval, a for .. in-loop is misschien een beter idee. Echter, moet u de juiste beveiligingen gebruiken om ervoor te zorgen dat alleen de gewenste eigenschappen van de array (dat wil zeggen, de arrayelementen) worden toegepast, aangezien de for..in-loop wordt ook opgesomd in oudere browsers, of als de aanvullende eigenschappen zijn gedefinieerd als enumerable.

In ECMAScript 5 er zal een forEach-methode zijn op het array-prototype, maar deze wordt niet ondersteund in oudere browsers. Dus om het consistent te kunnen gebruiken, moet je ofwel een omgeving hebben die dit ondersteunt (bijvoorbeeld Node.js voor JavaScript aan de serverzijde), of gebruik een "Polyfill". De Polyfill voor deze functionaliteit is echter triviaal en omdat het de code gemakkelijker leesbaar maakt, is het een goede polyfill om mee te nemen.


451
2018-02-17 13:55



Als u de jQuery bibliotheek, die u kunt gebruiken jQuery.each:

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

BEWERK : 

Per vraag wil de gebruiker code in javascript in plaats van jQuery, dus de bewerking is

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

205
2018-02-17 14:01



Loop achteruit

ik denk de omgekeerde for loop verdient hier een vermelding:

for (var i = array.length; i--; ) {
     // process array[i]
}

voordelen:

  • U hoeft geen tijdelijk te declareren len variabel, of vergelijken met array.length op elke iteratie, die beide een minuut optimalisatie kunnen zijn.
  • Broers en zussen verwijderen van de DOM in omgekeerde volgorde is meestal efficiënter. (De browser hoeft minder elementen in zijn interne arrays te verplaatsen.)
  • als jij wijzig de array tijdens het lussen, op of na de index ik (bijvoorbeeld als u een item verwijdert of invoegt bij array[i]), dan slaat een voorwaartse lus het item over dat links naar zijn positie is verschoven ik, of herwerk de ikhet item dat goed was verschoven. In een traditionele lus, jij kon bijwerken ik om naar het volgende item te wijzen dat verwerking nodig heeft - 1, maar het eenvoudigweg omkeren van de richting van iteratie is vaak een eenvoudigere en elegantere oplossing.
  • Evenzo bij het wijzigen of verwijderen genesteld DOM-elementen, verwerking in omgekeerde kan omzeil fouten. Overweeg bijvoorbeeld om de innerHTML van een bovenliggend knooppunt aan te passen voordat u met de onderliggende items omgaat. Tegen de tijd dat het kindknooppunt wordt bereikt, wordt het losgemaakt van het DOM en vervangen door een nieuw gemaakt kind wanneer de innerHTML van de ouder is geschreven.
  • Het is kortere typen en lezen, dan sommige van de andere beschikbare opties. Hoewel het verliest forEach() en naar ES6's for ... of.

nadelen:

  • Het verwerkt de items in omgekeerde volgorde. Als u een nieuwe array uit de resultaten bouwt of dingen op het scherm afdrukt, natuurlijk de uitvoer zal worden omgekeerd met betrekking tot de oorspronkelijke bestelling.
  • Het herhaaldelijk inbrengen van broers en zussen in de DOM als een eerste kind om hun volgorde te behouden is minder efficient. (De browser zou de dingen altijd gelijk moeten blijven houden.) Om DOM-knooppunten efficiënt en in volgorde te maken, hoeft u alleen maar naar voren te lussen en op de normale manier toe te voegen (en ook een "documentfragment" te gebruiken).
  • De reverse-lus is verwarrend voor junior-ontwikkelaars. (U kunt dit als een voordeel beschouwen, afhankelijk van uw vooruitzichten.)

Moet ik het altijd gebruiken?

Sommige ontwikkelaars gebruiken de omgekeerde lus standaard, tenzij er een goede reden is om vooruit te lopen.

Hoewel de prestatiewinsten meestal onbeduidend zijn, is het soort gekrijs:

"Doe dit gewoon voor elk item in de lijst, ik geef niet om de bestelling!"

In de praktijk echter wel niet eigenlijk een betrouwbare indicatie van intentie, omdat het niet te onderscheiden is van die gelegenheden wanneer u do geef om de bestelling, en echt doen nodig hebben om achteruit te lussen. Dus in feite zou een ander construct nodig zijn om nauwkeurig de "do not care" -intentie uit te drukken, iets dat momenteel niet beschikbaar is in de meeste talen, inclusief ECMAScript, maar dat bijvoorbeeld zou kunnen worden genoemd forEachUnordered().

Als de volgorde er niet toe doet, en rendement is een zorg (in de binnenste lus van een game- of animatiemotor), dan kan het acceptabel zijn om de reverse-lus te gebruiken als uw startpatroon. Onthoud gewoon dat je een reverse ziet voor lus in bestaande code betekent niet noodzakelijkerwijs dat de volgorde niet relevant is!

Het is beter om te gebruiken voor elk ()

Over het algemeen geldt voor code op een hoger niveau waar duidelijkheid en veiligheid zijn grotere zorgen, zou ik aanraden om te gebruiken Array::forEach als uw standaardpatroon:

  • Het is duidelijk om te lezen.
  • Het geeft dat aan ik zal niet worden verschoven binnen het blok (wat altijd een mogelijke verrassing is die zich in lang verbergt for en while lussen.)
  • Het geeft je een vrije ruimte voor sluitingen.
  • Het vermindert de lekkage van lokale variabelen en een toevallige botsing met (en mutatie van) externe variabelen.

Als je dan het omgekeerde ziet voor lus in je code, is dat een hint dat het om een ​​goede reden is omgekeerd (misschien een van de hierboven beschreven redenen). En het zien van een traditionele forward-for-loop kan erop wijzen dat er kan worden geschakeld.

(Als de bespreking van intentie geen steek houdt voor jou, dan kunnen jij en je code baat hebben bij het kijken naar de lezing van Crockford Programmeerstijl & je brein.)


Hoe werkt het?

for (var i = 0; i < array.length; i++) { ... }   // Forwards

for (var i = array.length; i--; )    { ... }   // Reverse

Dat merk je i-- is de middelste zin (waar we gewoonlijk een vergelijking zien) en de laatste clausule is leeg (waar we gewoonlijk zien i++). Dat betekent dat i-- wordt ook gebruikt als staat voor voortzetting. Cruciaal is dat het wordt uitgevoerd en gecontroleerd voor elke iteratie.

  • Hoe kan het beginnen bij array.length zonder te exploderen?

    Omdat i-- runs voor elke iteratie, bij de eerste iteratie zullen we het item daadwerkelijk benaderen array.length - 1 waarmee problemen worden voorkomen Array-out-of-bounds  undefined items.

  • Waarom stopt het niet met itereren vóór index 0?

    De lus stopt met itereren wanneer de voorwaarde i-- evalueert naar een valsey-waarde (als deze 0 oplevert).

    De truc is dat in tegenstelling tot --i, de achterstand i-- operator neemt af i maar levert de waarde op voor de afname. Uw console kan dit aantonen:

    > var i = 5; [i, i--, i];

    [5, 5, 4]

    Dus over de laatste iteratie, ik was eerder 1 en de i-- expressie verandert dit in 0 maar levert eigenlijk op 1 (waarheidsgetrouw), en dus gaat de conditie voorbij. Op de volgende iteratie i-- veranderingen ik naar -1 maar opbrengsten 0 (Falsey), waardoor de uitvoering onmiddellijk uit de kring valt.

    In de traditionele voorwaartse lus, i++ en ++i zijn uitwisselbaar (zoals Douglas Crockford opmerkt). Maar in het omgekeerde geval, omdat onze decrement ook onze voorwaardeuitdrukking is, moeten we ons houden aan i-- als we het item bij index 0 willen verwerken.


Trivia

Sommige mensen tekenen graag een pijltje in de tegenovergestelde richting for loop en eindig met een knipoog:

for (var i = array.length; i --> 0 ;) {

Credits gaan naar WYL voor het tonen van de voordelen en gruwelen van het omgekeerde voor lus.


88
2018-05-02 14:21



Sommige C-stijl talen gebruiken foreach om door opsommingen te bladeren. In JavaScript wordt dit gedaan met de for..in lusstructuur:

var index,
    value;
for (index in obj) {
    value = obj[index];
}

Er is een vangst. for..in door elk van de opsombare leden van het object en de leden van het prototype. Om te voorkomen dat u waarden leest die worden overgenomen door het prototype van het object, controleert u eenvoudig of de eigenschap tot het object behoort:

for (i in obj) {
    if (obj.hasOwnProperty(i)) {
        //do stuff
    }
}

Bovendien, ECMAScript 5 heeft toegevoegd forEach methode om Array.prototypedie kan worden gebruikt om op te sommen via een array met behulp van een terugdraaipunt (de polyfill bevindt zich in de documenten zodat u deze nog steeds kunt gebruiken voor oudere browsers):

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

Het is belangrijk om dat op te merken Array.prototype.forEach breekt niet wanneer de callback terugkeert false. jQuery en Underscore.js geef hun eigen variaties op each om lussen te bieden die kunnen worden kortgesloten.


71
2018-02-17 14:00



Als u een array wilt herhalen, gebruikt u de standaard driedelige for lus.

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

U kunt enkele prestatie-optimalisaties krijgen door caching myArray.length of er achterwaarts over itereren.


30
2018-02-17 13:55



EEN forEach implementatie (zie in 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
  }
);

25
2018-04-10 00:26



Als je het niet erg vindt om de array te legen:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

x bevat de laatste waarde van y en het zal uit de array worden verwijderd. Je kan ook gebruiken shift() die het eerste item van zal geven en verwijderen y.


25
2018-03-10 02:37



Ik weet dat dit een oude post is en er zijn al zoveel geweldige antwoorden. Voor een beetje meer volledigheid dacht ik dat ik er nog een zou gebruiken angularjs. Dit is natuurlijk alleen van toepassing als je Angular gebruikt, natuurlijk, maar toch wil ik het toch stellen.

angular.forEach neemt 2 argumenten en een optioneel derde argument. Het eerste argument is het object (array) om te itereren, het tweede argument is de iteratorfunctie en het optionele derde argument is de objectcontext (in de loop van de lus eigenlijk 'object' genoemd).

Er zijn verschillende manieren om de forEach-lus van hoekig te gebruiken. De eenvoudigste en waarschijnlijk meest gebruikte is

var temp = [1, 2, 3];
angular.forEach(temp, function(item) {
    //item will be each element in the array
    //do something
});

Een andere manier die handig is voor het kopiëren van items van de ene array naar de andere is

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

Hoewel, u hoeft dat niet te doen, u kunt eenvoudig het volgende doen en het komt overeen met het vorige voorbeeld:

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

Nu zijn er voor- en nadelen van het gebruik van de angular.forEach functie in tegenstelling tot de ingebouwde van vanille smaak for lus.

Pros

  • Eenvoudige leesbaarheid
  • Eenvoudige schrijfbaarheid
  • Indien beschikbaar, angular.forEach gebruikt de ES5 voor Elke lus. Nu ga ik naar de efficiëntie in de sectie Cons, zoals de forEach-lussen zijn veel langzamer dan de for-loops. Ik noem dit als een professional omdat het leuk is om consistent en gestandaardiseerd te zijn.

Beschouw de volgende 2 geneste lussen, die precies hetzelfde doen. Laten we zeggen dat we twee arrays van objecten hebben en dat elk object een reeks resultaten bevat, waarvan elk een waarde-eigenschap heeft die een string is (of wat dan ook). En laten we zeggen dat we de resultaten moeten herhalen en als ze gelijk zijn, voer dan een actie uit:

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

Toegegeven, dit is een heel simpel hypothetisch voorbeeld, maar ik heb triple embedded for loops geschreven met de tweede benadering en dat was het ook heel moeilijk te lezen, en schrijf voor die kwestie.

Cons

  • Efficiency. angular.forEachen de inboorling forEachtrouwens, beide zijn zo veel langzamer dan normaal for lus .... ongeveer 90% langzamer. Dus voor grote gegevenssets kunt u het beste bij de native blijven for lus.
  • Geen pauze, doorgaan of ondersteuning terugsturen. continue wordt feitelijk ondersteund door "ongeval", om verder te gaan in een angular.forEach je zet gewoon een return; verklaring in de functie zoals angular.forEach(array, function(item) { if (someConditionIsTrue) return; }); waardoor het uit de functie voor die iteratie zal blijven. Dit komt ook door het feit dat de autochtone forEach ondersteunt geen onderbreking of ga niet verder.

Ik ben er zeker van dat er ook verschillende andere voor- en nadelen zijn, en aarzel niet om er nog een toe te voegen die u geschikt lijkt. Ik denk dat, als het aankomt op efficiëntie, vasthoudt aan alleen de autochtone for loop voor uw looping-behoeften. Maar als uw datasets kleiner zijn en een bepaalde efficiëntie oké is om op te geven in ruil voor leesbaarheid en schrijfbaarheid, gooi dan in elk geval een angular.forEach in die slechte jongen.


25
2018-06-20 22:56