Loopa (för varje) över en array i JavaScript
Hur kan jag loopa genom alla poster i en array med JavaScript?
Svar #1
TL;DR
Dina bästa chanser är vanligtvis
en
for-of-loop (endast ES2015+; spec | MDN) - enkel ochasync-vänligfor (const element of theArray) { // ...use `element`... }
forEach(endast ES5+; spec | MDN) (eller dess släktingarsomeoch liknande) - inteasync-vänlig (men se detaljer)theArray.forEach(element => { // ...use `element`... });en enkel gammaldags
for-loop -async-vänligfor (let index = 0; index < theArray.length; ++index) { const element = theArray[index]; // ...use `element`... }(sällan)
for-inmed skyddsåtgärder -async-vänligfor (const propertyName in theArray) { if (/*...is an array element property (see below)...*/) { const element = theArray[propertyName]; // ...use `element`... } }
Några snabba "inte göra"-tips:
- Använd inte
for-inom du inte använder det med skydd eller åtminstone är medveten om varför det kan ställa till problem.
map om du inte använder dess returvärde.(Tyvärr finns det någon där ute som lär ut
map [spec/MDN] som om det vore forEach — men som jag skriver på min blogg, är det inte vad det är till för. Om du inte använder arrayen det skapar, använd inte map.)forEach om callbacken gör asynkront arbete och du vill att forEach ska vänta tills det arbetet är klart (för det kommer den inte att göra).Men det finns mycket mer att utforska, fortsätt läsa...
JavaScript har kraftfull semantik för att loopa genom arrayer och array-liknande objekt. Jag har delat upp svaret i två delar: Alternativ för riktiga arrayer, och alternativ för saker som bara är array-liknande, såsom arguments-objektet, andra itererbara objekt (ES2015+), DOM-samlingar och så vidare.
Okej, låt oss titta på våra alternativ:
För faktiska arrayer
Du har fem alternativ (två stöds i princip för alltid, ett annat lades till av ECMAScript 5 ["ES5"], och två till lades till i ECMAScript 2015 ("ES2015", även kallat "ES6")):
- Använd
for-of(använd en iterator implicit) (ES2015+) - Använd
forEachoch relaterade (ES5+) - Använd en enkel
for-loop - Använd
for-inkorrekt - Använd en iterator uttryckligen (ES2015+)
(Du kan se de gamla specifikationerna här: ES5, ES2015, men båda har ersatts; den aktuella redaktörens utkast finns alltid här.)
Detaljer :
1. Använd for-of (använd en iterator implicit) (ES2015+)
ES2015 lade till iteratorer och itererbara objekt i JavaScript. Arrayer är itererbara (det är även strängar, Map-objekt och Set-objekt, liksom DOM-samlingar och listor, som du kommer att se senare). Itererbara objekt tillhandahåller iteratorer för sina värden. Det nya for-of-uttrycket loopar genom de värden som returneras av en iterator:
const a = ["a", "b", "c"];
for (const element of a) { // You can use `let` instead of `const` if you like
console.log(element);
}
// a
// b
// cDet blir inte enklare än så! Under huven får det en iterator från arrayen och loopar genom de värden som iteratorn returnerar. Iteratorn som tillhandahålls av arrayer ger värdena av arrayelementen, i ordning från början till slut.
Observera hur element är begränsat till varje loop-iteration; att försöka använda element efter slutet av loopen skulle misslyckas eftersom det inte finns utanför loopens kropp.
I teorin involverar en for-of-loop flera funktionsanrop (ett för att få iteratorn, sedan ett för att få varje värde från den). Även när det är sant är det inget att oroa sig för, funktionsanrop är mycket billiga i moderna JavaScript-motorer (det störde mig för forEach [nedan] tills jag undersökte det; detaljer). Men dessutom optimerar JavaScript-motorer bort dessa anrop (i prestandakritisk kod) när de hanterar inbyggda iteratorer för saker som arrayer.
for-of är helt async-vänligt. Om du behöver att arbetet i en loopkropp görs i serie (inte parallellt), kommer ett await i loopkroppen att vänta på att promisen ska lösas innan det fortsätter. Här är ett fånigt exempel:
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
for (const message of messages) {
await delay(400);
console.log(message);
}
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejectsObservera hur orden visas med en fördröjning före varje ord.
Det är en fråga om kodningsstil, men for-of är det första jag tar till när jag loopar igenom något itererbart.
2. Använd forEach och relaterade
I en vilken som helst ens lite modern miljö (så, inte IE8) där du har tillgång till Array-funktionerna som lades till i ES5, kan du använda forEach (spec | MDN) om du bara hanterar synkron kod (eller om du inte behöver vänta på att en asynkron process ska slutföras under loopen):
const a = ["a", "b", "c"];
a.forEach((element) => {
console.log(element);
});forEach accepterar en callback-funktion och, valfritt, ett värde att använda som this när den callbacken anropas (används inte ovan). Callbacken anropas för varje element i arrayen, i ordning, och hoppar över icke-existerande element i glest fyllda arrayer. Även om jag bara använde en parameter ovan, anropas callbacken med tre argument: Elementet för den iterationen, indexet för det elementet och en referens till arrayen du itererar över (i fall din funktion inte redan har det tillgängligt).
Precis som for-of, har forEach fördelen att du inte behöver deklarera indexerings- och värdevariabler i den omgivande scopet; i det här fallet tillhandahålls de som argument till iterationsfunktionen, och är därmed fint avgränsade till just den iterationen.
Till skillnad från for-of har forEach nackdelen att den inte förstår async-funktioner och await. Om du använder en async-funktion som callback väntar forEach inte på att den funktionens promise ska lösas innan den fortsätter. Här är async-exemplet från for-of som använder forEach istället — notera hur det finns en initial fördröjning, men sedan visas all text direkt istället för att vänta:
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
// INCORRECT, doesn't wait before continuing,
// doesn't handle promise rejections
messages.forEach(async message => {
await delay(400);
console.log(message);
});
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejectsforEach är funktionen för att "loopa igenom alla", men ES5 definierade flera andra användbara funktioner för att "arbeta sig igenom arrayen och göra saker", inklusive:
every(spec | MDN) - slutar loopa första gången callback-funktionen returnerar ett falskt värdesome(spec | MDN) - slutar loopa första gången callback-funktionen returnerar ett värde som är santfilter(spec | MDN) - skapar en ny array som inkluderar element där callbacken returnerar ett sanningsvärde, och utelämnar de där den inte gör detmap(spec | MDN) - skapar en ny array från värdena som returneras av callbackfunktionenreduce(spec | MDN) - bygger upp ett värde genom att upprepade gånger anropa callback-funktionen och skicka in tidigare värden; se specen för detaljernareduceRight(spec | MDN) - somreduce, men fungerar i nedåtgående istället för uppåtgående ordning
Precis som med forEach, om du använder en async-funktion som din callback, väntar ingen av dem på att funktionen ska slutföra sitt löfte. Det betyder:
- Att använda en
async-funktionsåteruppringning är aldrig lämpligt medevery,someochfiltereftersom de kommer att behandla den returnerade löftet som om det vore ett sant värde; de väntar inte på att löftet ska fullbordas och använder sedan fullbordandets värde. - Att använda en
asyncfunktionsåteranrop är ofta lämpligt medmap, om målet är att omvandla en array av något till en array av promises, kanske för att skicka till en av promise-kombinatorfunktionerna (Promise.all,Promise.race,Promise.allSettledellerPromise.any). - Att använda en
asyncfunktionscallback är sällan lämpligt medreduceellerreduceRight, eftersom (återigen) callbacken alltid kommer att returnera ett löfte. Men det finns ett uttryck för att bygga en kedja av löften från en array som använderreduce(const promise=array.reduce((p, element) => p.then(/*...något som använder `element`...*/));), men vanligtvis i de fallen kommer enfor-ofellerforloop i enasyncfunktion att vara tydligare och lättare att debugga.
3. Använd en enkel for-slinga
Ibland är de gamla sätten bäst:
const a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
const element = a[index];
console.log(element);
}Om längden på arrayen inte kommer att ändras under loopen, och den är i kod som är mycket prestandakritisk, kan en något mer komplicerad version som hämtar längden i förväg vara en liten aning snabbare:
const a = ["a", "b", "c"];
for (let index = 0, len = a.length; index < len; ++index) {
const element = a[index];
console.log(element);
}Och/eller räkna baklänges:
const a = ["a", "b", "c"];
for (let index = a.length - 1; index >= 0; --index) {
const element = a[index];
console.log(element);
}Men med moderna JavaScript-motorer är det sällan du behöver få ut den sista lilla biten kraft.
Innan ES2015 var loopvariabeln tvungen att finnas i den omgivande scope, eftersom var bara har funktionsnivå-scope, inte blocknivå-scope. Men som du såg i exemplen ovan kan du använda let inom for för att låsa variablerna till endast loopen. Och när du gör det, återskapas index-variabeln för varje looppiteration, vilket betyder att closures som skapas i loopens kropp behåller en referens till index för just den iterationen, vilket löser det gamla "closures i loopar"-problemet:
// (The `NodeList` from `querySelectorAll` is array-like)
const 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>I ovanstående får du "Index är: 0" om du klickar på den första och "Index är: 4" om du klickar på den sista. Detta fungerar inte om du använder var istället för let (du skulle alltid se "Index är: 5").
Precis som for-of fungerar for-loopar bra i async-funktioner. Här är det tidigare exemplet som använder en for-loop:
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
for (let i = 0; i < messages.length; ++i) {
const message = messages[i];
await delay(400);
console.log(message);
}
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects4. Använd for-in korrekt
for-in är inte för att loopa igenom arrayer, det är för att loopa igenom namnen på ett objekts egenskaper. Det verkar ofta fungera för att loopa igenom arrayer som en bieffekt av att arrayer är objekt, men det loopar inte bara igenom arrayens index, det loopar igenom alla uppräkningsbara egenskaper hos objektet (inklusive ärvda). (Det brukade också vara så att ordningen inte var specificerad; den är det nu [detaljer i detta andra svar], men även om ordningen är specificerad nu, är reglerna komplexa, det finns undantag, och att förlita sig på ordningen är inte bästa praxis.)
De enda verkliga användningsområdena för for-in på en array är:
- Det är en gles matris med massiva luckor i den, eller
- Du använder icke-elementegenskaper på array-objektet och du vill inkludera dem i loopen
Om vi bara tittar på det första exemplet: Du kan använda for-in för att besöka dessa glest befolkade arrayelement om du använder lämpliga skyddsåtgärder:
// `a` is a sparse array
const a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (const name in a) {
if (Object.hasOwn(a, name) && // These checks are
/^0$|^[1-9]\d*$/.test(name) && // explained
name <= 4294967294 // below
) {
const element = a[name];
console.log(a[name]);
}
}Observera de tre kontrollerna:
Att objektet har sin egen egenskap med det namnet (inte en som det ärvt från sitt prototyp; denna kontroll skrivs ofta också som
a.hasOwnProperty(name)men ES2022 lägger tillObject.hasOwnsom kan vara mer pålitlig), ochAtt namnet består av enbart decimala siffror (t.ex. normal strängform, inte vetenskaplig notation), och
Att värdet på namnet när det omvandlas till ett tal är <= 2^32 - 2 (vilket är 4,294,967,294). Var kommer det talet ifrån? Det är en del av definitionen av ett arrayindex i specifikationen. Andra tal (icke-heltal, negativa tal, tal större än 2^32 - 2) är inte arrayindex. Anledningen till att det är 2^32 - 2 är att det gör att det största indexvärdet blir ett lägre än 2^32 - 1, vilket är det högsta värde en arrays
lengthkan ha. (Till exempel, en arrays längd ryms i ett 32-bitars osignerat heltal.)
...även om det sagt, gör de flesta koder bara hasOwnProperty-kontrollen.
Du skulle naturligtvis inte göra det i inline-kod. Du skulle skriva en hjälpmetod. Kanske:
// Utility function for antiquated environments without `forEach`
const hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
const rexNum = /^0$|^[1-9]\d*$/;
function sparseEach(array, callback, thisArg) {
for (const name in array) {
const index = +name;
if (hasOwn(a, name) &&
rexNum.test(name) &&
index <= 4294967294
) {
callback.call(thisArg, array[name], index, array);
}
}
}
const a = [];
a[5] = "five";
a[10] = "ten";
a[100000] = "one hundred thousand";
a.b = "bee";
sparseEach(a, (value, index) => {
console.log("Value at " + index + " is " + value);
});Precis som for fungerar for-in bra i asynkrona funktioner om arbetet inuti det behöver göras i serie.
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
for (const name in messages) {
if (messages.hasOwnProperty(name)) { // Almost always this is the only check people do
const message = messages[name];
await delay(400);
console.log(message);
}
}
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects5. Använd en iterator uttryckligen (ES2015+)
for-of använder en iterator implicit och gör allt det tråkiga arbetet åt dig. Ibland kanske du vill använda en iterator explicit. Det ser ut så här:
const a = ["a", "b", "c"];
const it = a.values(); // Or `const it = a[Symbol.iterator]();` if you like
let entry;
while (!(entry = it.next()).done) {
const element = entry.value;
console.log(element);
}En iterator är ett objekt som matchar Iterator-definitionen i specifikationen. Dess next-metod returnerar ett nytt resultatobjekt varje gång du kallar på det. Resultatobjektet har en egenskap, done, som talar om för oss om det är klart, och en egenskap value med värdet för den iterationen. (done är valfri om den skulle vara false, value är valfri om den skulle vara undefined.)
Vad du får för value varierar beroende på iteratorn. På arrayer tillhandahåller standarditeratorn värdet för varje arrayelement ("a", "b" och "c" i det tidigare exemplet). Arrayer har också tre andra metoder som returnerar iteratorer:
values(): Detta är ett alias för[Symbol.iterator]-metoden som returnerar standarditeratorn.keys(): Returnerar en iterator som tillhandahåller varje nyckel (index) i arrayen. I exemplet ovan skulle den ge0, sedan1, sedan2(som siffror, inte som strängar). (Observera också att i en gles array kommer den att inkludera index för element som inte existerar.)entries(): Returnerar en iterator som tillhandahåller[key, value]-arrayer.
Eftersom iteratorobjekt inte går framåt förrän du anropar next, fungerar de bra i async-funktionsloopar. Här är det tidigare for-of-exemplet som använder iteratorn explicit:
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
const it = messages.values()
while (!(entry = it.next()).done) {
await delay(400);
const element = entry.value;
console.log(element);
}
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejectsFör array-liknande objekt
Förutom riktiga arrayer finns det också array-liknande objekt som har en length-egenskap och egenskaper med helt numeriska namn: NodeList-instanser, HTMLCollection-instanser, arguments-objektet, osv. Hur loopar vi igenom deras innehåll?
Använd de flesta av alternativen ovan
Åtminstone några, och möjligtvis de flesta eller till och med alla, av de ovanstående metoderna för arrayer gäller lika bra för arrayliknande objekt:
Använd
for-of(använd en iterator implicit) (ES2015+)for-ofanvänder iteratorn som tillhandahålls av objektet (om någon finns). Det inkluderar objekt som tillhandahålls av värden (som DOM-samlingar och listor). Till exempel stöderHTMLCollection-instanser frångetElementsByXYZ-metoder ochNodeList-instanser frånquerySelectorAllbåda iteration. (Detta definieras ganska subtilt av HTML- och DOM-specifikationerna. I princip är alla objekt medlengthoch indexerad åtkomst automatiskt itererbara. Det behöver inte markeras somiterable; det används endast för samlingar som, utöver att vara itererbara, stöderforEach,values,keysochentries-metoder.NodeListgör det;HTMLCollectiongör det inte, men båda är itererbara.)Här är ett exempel på att loopa igenom
div-element:
const divs = document.querySelectorAll("div");
for (const div of divs) {
div.textContent = Math.random();
}<div>zero</div>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>Använd
forEachoch relaterade (ES5+)De olika funktionerna på
Array.prototypeär "avsiktligt generiska" och kan användas på array-liknande objekt viaFunction#call(spec | MDN) ellerFunction#apply(spec | MDN). (Om du måste hantera IE8 eller tidigare [aj], se "Varning för objekts som tillhandahålls av värden" i slutet av detta svar, men det är inte ett problem med någorlunda moderna webbläsare.)Anta att du ville använda
forEachpå enNodeschildNodes-samling (som, eftersom det är enHTMLCollection, inte harforEachinbyggt). Du skulle göra så här:Array.prototype.forEach.call(node.childNodes, (child) => { // Do something with `child` });(Notera dock att du faktiskt bara kan använda
for-ofpånode.childNodes.)Om du ska göra det ofta, kanske du vill ta en kopia av funktionsreferensen till en variabel för återanvändning, t.ex.:
// (This is all presumably in a module or some scoping function) const forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); // Then later... forEach(node.childNodes, (child) => { // Do something with `child` });Använd en enkel
for-loopKanske uppenbart fungerar en enkel
for-loop för arrayliknande objekt.Använd en iterator explicit (ES2015+)
Se #1.
Du kan kanske komma undan med for-in (med försiktighetsåtgärder), men med alla dessa mer lämpliga alternativ finns det ingen anledning att försöka.
Skapa en riktig array
Andra gånger kanske du vill omvandla ett array-liknande objekt till en riktig array. Att göra det är överraskande enkelt:
Använd
Array.fromArray.from(spec) | (MDN) (ES2015+, men lätt att polyfilla) skapar en array från ett array-liknande objekt, och kan valfritt skicka posterna genom en mappingsfunktion först. Så:const divs = Array.from(document.querySelectorAll("div"));...tar
NodeListfrånquerySelectorAlloch gör en array av den.Mappningsfunktionen är praktisk om du skulle vilja mappa innehållet på något sätt. Till exempel, om du ville få en array med taggnamnen på elementen med en viss klass:
// Typical use (with an arrow function): const divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName); // Traditional function (since `Array.from` can be polyfilled): var divs = Array.from(document.querySelectorAll(".some-class"), function(element) { return element.tagName; });Använd spridsyntax (
...)Det är också möjligt att använda ES2015:s spread-syntax. Liksom
for-ofanvänder detta iteratorn som tillhandahålls av objektet (se #1 i föregående avsnitt):const trueArray = [...iterableObject];Så till exempel, om vi vill konvertera en
NodeListtill en riktig array, blir detta med spridningssyntax ganska kortfattat:const divs = [...document.querySelectorAll("div")];Använd
slice-metoden för arrayerVi kan använda
slice-metoden för arrayer, som precis som de andra metoderna som nämnts ovan är "avsiktligt generell" och därför kan användas med array-liknande objekt, så här:const trueArray = Array.prototype.slice.call(arrayLikeObject);Så till exempel, om vi vill konvertera en
NodeListtill en riktig array, skulle vi kunna göra detta:const divs = Array.prototype.slice.call(document.querySelectorAll("div"));(Om du fortfarande måste hantera IE8 [aj], kommer det att misslyckas; IE8 tillät dig inte att använda värd-levererade objekt som
thispå det sättet.)
Varning för värdlevererade objekt
Om du använder Array.prototype-funktioner med värdlevererade array-liknande objekt (till exempel DOM-samlingar och sådana som tillhandahålls av webbläsaren snarare än JavaScript-motorn), hanterade föråldrade webbläsare som IE8 det inte nödvändigtvis på det sättet, så om du måste stödja dem, se till att testa i dina målmiljöer. Men det är inte ett problem med relativt moderna webbläsare. (För icke-webbläsarmiljöer, beror det naturligtvis på miljön.)
Svar #2
Observera: Detta svar är hopplöst föråldrat. För en mer modern metod, titta på de metoder som finns tillgängliga för en array. Metoder av intresse kan vara:
- förVarje
- karta
- filter
- reducera
- varje
- några
Det standardmässiga sättet att iterera över en array i JavaScript är en vanlig for-loop:
var length = arr.length,
element = null;
for (var i = 0; i < length; i++) {
element = arr[i];
// Do something with element
}
Observera dock att detta tillvägagångssätt bara är bra om du har en tät array, och varje index är upptaget av ett element. Om arrayen är gles kan du stöta på prestandaproblem med detta tillvägagångssätt, eftersom du kommer att iterera över många index som inte verkligen finns i arrayen. I detta fall kan en for .. in-loop vara en bättre idé. Dock måste du använda lämpliga säkerhetsåtgärder för att säkerställa att endast de önskade egenskaperna i arrayen (det vill säga arrayelementen) påverkas, eftersom for..in-loopen också kommer att räkna upp i äldre webbläsare, eller om de ytterligare egenskaperna är definierade som enumerable.
I ECMAScript 5 kommer det att finnas en forEach-metod på array-prototypen, men den stöds inte i äldre webbläsare. Så för att kunna använda den konsekvent måste du antingen ha en miljö som stöder den (till exempel Node.js för serverside JavaScript), eller använda en "Polyfill". Polyfillen för denna funktionalitet är dock trivial och eftersom den gör koden lättare att läsa, är det en bra polyfill att inkludera.
Svar #3
Om du använder jQuery-biblioteket kan du använda jQuery.each:
$.each(yourArray, function(index, value) {
// do your stuff here
});
REDIGERA :
Enligt frågan vill användaren kod i JavaScript istället för jQuery, så ändringen är
var length = yourArray.length;
for (var i = 0; i < length; i++) {
// Do something with yourArray[i].
}
Svar #4
Loopa bakåt
Jag tycker att omvända for-loop förtjänar ett omnämnande här:
for (var i = array.length; i--; ) {
// process array[i]
}
Fördelar:
- Du behöver inte deklarera en temporär
len-variabel, eller jämföra medarray.lengthvid varje iteration, vilket vardera kan vara en liten optimering. - Ta bort syskon från DOM:en i omvänd ordning är vanligtvis mer effektivt. (Webbläsaren behöver göra mindre förskjutning av element i dess interna arrayer.)
- Om du ändrar arrayen medan du loopar, vid eller efter index i (till exempel om du tar bort eller lägger till ett objekt vid
array[i]), kommer en framåtriktad loop att hoppa över det objekt som flyttats till vänster till position i, eller bearbeta ite objektet som flyttats till höger igen. I en traditionell for-loop kan du uppdatera i för att peka på nästa objekt som behöver bearbetas - 1, men att helt enkelt vända riktningen på iterationen är ofta en enklare och mer elegant lösning. - På samma sätt, när man ändrar eller tar bort inbäddade DOM-element, kan bearbetning i omvänd ordning omgå fel. Till exempel, överväg att ändra innerHTML för en föräldranod innan man hanterar dess barn. När barnnoden nås kommer den att vara frånkopplad från DOM, efter att ha ersatts av ett nyss skapat barn när förälderns innerHTML skrevs.
- Det är kortare att skriva och läsa än några av de andra alternativ som finns tillgängliga. Även om det förlorar mot
forEach()och ES6:sfor ... of.
Nackdelar:
- Den bearbetar objekten i omvänd ordning. Om du byggde en ny matris från resultaten, eller skrev ut saker på skärmen, kommer naturligtvis utgången att vara omvänd i förhållande till den ursprungliga ordningen.
- Att upprepade gånger infoga syskon i DOM som första barn för att behålla deras ordning är mindre effektivt. (Webbläsaren skulle hela tiden behöva flytta saker åt höger.) För att skapa DOM-noder effektivt och i ordning, loopa helt enkelt framåt och lägg till som vanligt (och använd även ett "dokumentfragment").
- Den omvända loopen är förvirrande för juniora utvecklare. (Du kan se det som en fördel, beroende på ditt synsätt.)
Ska jag alltid använda det?
Vissa utvecklare använder den omvända for-loopen som standard, om det inte finns en god anledning att loopa framåt.
Även om prestandaförbättringarna vanligtvis är obetydliga, skriker det på något sätt:
"Gör bara detta på varje objekt i listan, jag bryr mig inte om ordningen!"
Men i praktiken är det inte faktiskt en pålitlig indikation på avsikt, eftersom det är omöjligt att skilja från de tillfällen när du faktiskt bryr dig om ordningen, och verkligen behöver loopa i omvänd ordning. Så i själva verket skulle ett annat konstruktion behövas för att korrekt uttrycka avsikten 'bry sig inte', något som för närvarande inte finns i de flesta språk, inklusive ECMAScript, men som till exempel skulle kunna kallas forEachUnordered().
Om ordningen inte spelar någon roll, och effektivitet är en fråga (i den innersta loopen i ett spel- eller animationsprogram), kan det vara acceptabelt att använda den omvända for-loopen som ditt standardmönster. Kom bara ihåg att att se en omvänd for-loop i befintlig kod inte nödvändigtvis betyder att ordningen är irrelevant!
Det var bättre att använda forEach()
Generellt, för högre nivå kod där tydlighet och säkerhet är viktigare, har jag tidigare rekommenderat att använda Array::forEach som ditt standardmönster för loopar (även om jag nuförtiden föredrar att använda for..of). Anledningar att föredra forEach framför en omvänd loop är:
- Det är tydligare att läsa.
- Det indikerar att i inte kommer att flyttas inom blocket (vilket alltid är en möjlig överraskning som kan dyka upp i långa
forochwhile-loopar). - Det ger dig ett fritt omfång för closures.
- Det minskar läckage av lokala variabler och oavsiktlig kollision med (och förändring av) yttre variabler.
Då när du ser den omvända for-loopen i din kod, är det en ledtråd på att den är omvänd av en god anledning (kanske en av anledningarna som beskrivs ovan). Och att se en traditionell framåtriktad for-loop kan indikera att förskjutning kan äga rum.
(Om diskussionen om avsikt inte verkar meningsfull för dig, kan både du och din kod ha nytta av att titta på Crockfords föreläsning om Programmeringsstil och Din Hjärna.)
Det är nu ännu bättre att använda for..of!
Det pågår en debatt om huruvida for..of eller forEach() är att föredra:
För maximal webbläsarstöd kräver
for..ofen polyfill för iteratorer, vilket gör att din app körs något långsammare och blir något större att ladda ner.Av den anledningen (och för att uppmuntra användning av
mapochfilter) förbjuder vissa front-end-stilguiderfor..ofhelt!Men ovanstående bekymmer gäller inte Node.js-applikationer, där
for..ofnu är väl stödd.Och dessutom fungerar
awaitinte inutiforEach(). Att användafor..ofär det tydligaste mönstret i detta fall.
Personligen tenderar jag att använda det som verkar lättast att läsa, om inte prestanda eller minimering har blivit en stor fråga. Så nuförtiden föredrar jag att använda for..of istället för forEach(), men jag kommer alltid att använda map eller filter eller find eller some när det är tillämpligt. (För mina kollegors skull använder jag sällan reduce.)
Hur fungerar det?
for (var i = 0; i < array.length; i++) { ... } // Forwards
for (var i = array.length; i--; ) { ... } // Reverse
Du kommer att märka att i-- är mittenklassen (där vi vanligtvis ser en jämförelse) och att den sista klassen är tom (där vi vanligtvis ser i++). Det betyder att i-- också används som villkoret för fortsatt körning. Avgörande är att den utförs och kontrolleras före varje iteration.
Hur kan det starta vid
array.lengthutan att explodera?Eftersom
i--körs före varje iteration, kommer vi vid första iterationen faktiskt att komma åt objektet vidarray.length - 1, vilket undviker eventuella problem medArray-out-of-boundsundefined-objekt.Varför slutar det inte iterera före index 0?
Loopen kommer att sluta iterera när villkoret
i--utvärderas till ett falskt värde (när det ger 0).Tricket är att till skillnad från
--i, minskar den avslutandei---operatornimen ger värdet före minskningen. Din konsol kan visa detta:> var i = 5; [i, i--, i];[5, 5, 4]Så på den sista iterationen var i tidigare 1 och
i---uttrycket ändrar det till 0 men returnerar faktiskt 1 (sant), och villkoret uppfylls därför. På nästa iteration ändrari--i till -1 men returnerar 0 (falskt), vilket gör att exekveringen omedelbart lämnar loopens nederkant.I den traditionella for-loop i framåt riktning är
i++och++iutbytbara (som Douglas Crockford påpekar). Men i den omvända for-loopen, eftersom vår minskning också är vårt villkorsuttryck, måste vi hålla oss tilli--om vi vill bearbeta objektet vid index 0.
Kuriosa
Vissa människor gillar att rita en liten pil i den omvända for-loopen och avsluta med en blinkning:
for (var i = array.length; i --> 0 ;) {
Krediter går till WYL för att ha visat mig fördelarna och skräckerna med den omvända for-loopen.
Svar #5
Vissa C-stil språk använder foreach för att loopa genom uppräkningar. I JavaScript görs detta med for..in-loopstrukturen:
var index,
value;
for (index in obj) {
value = obj[index];
}
Det finns en hake. for..in kommer att loopa genom varje objektets uppräkningsbara medlemmar, samt medlemmarna på dess prototyp. För att undvika att läsa värden som är ärvda genom objektets prototyp, kontrollera helt enkelt om egenskapen tillhör objektet:
for (i in obj) {
if (obj.hasOwnProperty(i)) {
//do stuff
}
}
Dessutom har ECMAScript 5 lagt till en forEach-metod till Array.prototype som kan användas för att loopa över en array med en callback (polyfillen finns i dokumentationen så att du fortfarande kan använda den för äldre webbläsare):
arr.forEach(function (val, index, theArray) {
//do stuff
});
Det är viktigt att notera att Array.prototype.forEach inte avbryts när callback-funktionen returnerar false. jQuery och Underscore.js tillhandahåller sina egna varianter av each för att kunna göra loopar som kan avbrytas.
Svar #6
för...av | forEach | map
Att använda modern JavaScript-syntax för att iterera genom arrayer
const fruits = ['🍎', '🍋', '🍌' ]
👉🏽 för...av
for (const fruit of fruits) {
console.log(fruit) // '🍎', '🍋', '🍌'
}
👉🏽 förVarje
fruits.forEach(fruit => {
console.log(fruit) // '🍎', '🍋', '🍌'
})
👉🏽 karta
*Annorlunda än de två ovan, skapar map() en ny array och förväntar sig att du returnerar något efter varje iteration.
fruits.map(fruit => fruit) // ['🍎', '🍋', '🍌' ]
🛑 Viktigt: Eftersom map() är avsedd att returnera ett värde vid varje iteration, är det en idealisk metod för att transformera element i arrayer:
fruits.map(fruit => 'cool ' + fruit) // ['cool 🍎', 'cool 🍋', 'cool 🍌' ]
Å andra sidan behöver for...of och forEach( ) inte returnera något och det är därför vi vanligtvis använder dem för att utföra logikuppgifter som manipulerar saker utanför.
Så att säga, du kommer att hitta if()-satser, bieffekter och loggningsaktiviteter i dessa två.
👌🏾 TIPS: du kan också ha indexet (såväl som hela arrayen) i varje iteration i dina .map()- eller .forEach()-funktioner.
Bara skicka ytterligare argument till dem:
fruits.map((fruit, i) => i + ' ' + fruit)
// ['0 🍎', '1 🍋', '2 🍌' ]
fruits.forEach((f, i, arr) => {
console.log( f + ' ' + i + ' ' + arr )
})
// 🍎 0 🍎, 🍋, 🍌,
// 🍋 1 🍎, 🍋, 🍌,
// 🍌 2 🍎, 🍋, 🍌,
Svar #7
Om du vill loopa över en array, använd den standardiserade tredelade for-loopen.
for (var i = 0; i < myArray.length; i++) {
var arrayItem = myArray[i];
}
Du kan få vissa prestandaförbättringar genom att cachelagra myArray.length eller iterera över det baklänges.
Svar #8
Om du inte har något emot att tömma arrayen:
var x;
while(x = y.pop()){
alert(x); //do something
}
x kommer att innehålla det sista värdet av y och det kommer att tas bort från arrayen. Du kan också använda shift() som kommer att ge och ta bort det första elementet från y.
Svar #9
En forEach-implementation (se i 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
}
);
Svar #10
Jag vet att detta är ett gammalt inlägg, och det finns redan så många fantastiska svar. För lite mer fullständighet tänkte jag lägga till ett till med AngularJS. Självklart gäller detta endast om du använder Angular, uppenbarligen, men jag skulle ändå vilja lägga till det.
angular.forEach tar 2 argument och ett valfritt tredje argument. Det första argumentet är objektet (arrayen) att iterera över, det andra argumentet är iteratorfunktionen, och det valfria tredje argumentet är objektets kontext (refereras i princip till inuti loopen som 'this').
Det finns olika sätt att använda Angulars forEach-loop. Den enklaste och troligen mest använda är
var temp = [1, 2, 3];
angular.forEach(temp, function(item) {
//item will be each element in the array
//do something
});
Ett annat sätt som är användbart för att kopiera objekt från en array till en annan är
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);
Men du behöver inte göra det, du kan helt enkelt göra följande och det är ekvivalent med det tidigare exemplet:
angular.forEach(temp, function(item) {
temp2.push(item);
});
Nu finns det fördelar och nackdelar med att använda angular.forEach-funktionen istället för den inbyggda vaniljsmakade for-slingan.
Fördelar
- Lättläst
- Lätt att skriva
- Om tillgängligt kommer
angular.forEachatt använda ES5 förEach-loopen. Nu kommer jag att ta upp effektivitet i nackdelarna, eftersom forEach-looparna är mycket långsammare än for-looparna. Jag nämner detta som en fördel eftersom det är trevligt att vara konsekvent och standardiserad.
Betrakta följande 2 nästlade loopar, som gör exakt samma sak. Låt oss säga att vi har 2 arrayer med objekt och varje objekt innehåller en array med resultat, som var och en har en Value-egenskap som är en sträng (eller vad som helst). Och låt oss säga att vi behöver iterera över varje resultat och om de är lika, utföra någon åtgärd:
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
}
}
}
Visserligen är detta ett mycket enkelt hypotetiskt exempel, men jag har skrivit trippelt inbäddade for-loopar med den andra metoden och det var väldigt svårt att läsa, och skriva för den delen.
Nackdelar
- Effektivitet.
angular.forEachoch den inbyggdaforEach, för den delen, är båda så mycket långsammare än den vanligafor-loopen....ungefär 90% långsammare. Så för stora datamängder är det bäst att hålla sig till den inbyggdafor-loopen. - Ingen break-, continue- eller return-stöd.
continuestöds faktiskt av "olyckshändelse", för att fortsätta i enangular.forEachsätter du helt enkelt ettreturn;-uttalande i funktionen somangular.forEach(array, function(item) { if (someConditionIsTrue) return; });vilket kommer att göra att den fortsätter ut ur funktionen för den iterationen. Detta beror också på att den inbyggdaforEachinte heller stöder break eller continue.
Jag är säker på att det finns olika andra för- och nackdelar också, och var vänlig och lägg till de som du tycker passar. Jag känner att, i slutändan, om du behöver effektivitet, håll dig bara till den inbyggda for-slingan för dina loop-behov. Men om dina datamängder är mindre och lite effektivitet kan offras i utbyte mot läsbarhet och skrivbarhet, då får du absolut slänga in en angular.forEach i den där snyggingen.
Svar #11
Från och med ECMAScript 6:
list = [0, 1, 2, 3]
for (let obj of list) {
console.log(obj)
}Där of undviker underligheterna som är förknippade med in och gör att det fungerar som for-loopen i vilket annat språk som helst, och let binder i inom loopen istället för inom funktionen.
Klamrarna ({}) kan utelämnas när det bara finns ett kommando (t.ex. i exemplet ovan).
Svar #12
Sannolikt är for(i=0; i < array.length; i++)-loopen inte det bästa valet. Varför? Om du har detta:
var array = new Array();
array[1] = "Hello";
array[7] = "World";
array[11] = "!";
Metoden kommer att anropa från array[0] till array[2]. För det första kommer detta först att referera till variabler du inte ens har, för det andra skulle du inte ha variablerna i arrayen, och för det tredje kommer detta att göra koden djärvare. Titta här, det är vad jag använder:
for(var i in array){
var el = array[i];
//If you want 'i' to be INT just put parseInt(i)
//Do something with el
}
Och om du vill att det ska vara en funktion, kan du göra så här:
function foreach(array, call){
for(var i in array){
call(array[i]);
}
}
Om du vill ta en paus, lite mer logik:
function foreach(array, call){
for(var i in array){
if(call(array[i]) == false){
break;
}
}
}
Exempel :
foreach(array, function(el){
if(el != "!"){
console.log(el);
} else {
console.log(el+"!!");
}
});
Det returnerar:
//Hello
//World
//!!!
Svar #13
Det finns tre implementationer av foreach i jQuery enligt följande.
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
Svar #14
En enkel lösning nu skulle vara att använda biblioteket underscore.js. Det erbjuder många användbara verktyg, såsom each och kommer automatiskt att delegera jobbet till den inbyggda forEach om den är tillgänglig.
Ett CodePen-exempel på hur det fungerar är:
var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});
Se även
- Dokumentation för inbyggda
Array.prototype.forEach(). - I for_each...in (MDN) förklaras det att
for each (variable in object)är föråldrat som en del av ECMA-357 (EAX) standarden. - for...of (MDN) beskriver nästa sätt att iterera med
for (variable of object)som en del av Harmony (ECMAScript 6) förslaget.
Svar #15
Det finns ingen for each-loop i inbyggd JavaScript. Du kan antingen använda bibliotek för att få denna funktionalitet (jag rekommenderar Underscore.js), använda en enkel for-in-loop.
for (var instance in objects) {
...
}
Observera dock att det kan finnas skäl att använda en ännu enklare for-loop (se Stack Overflow-frågan Varför är det en dålig idé att använda "for…in" vid array-iteration?)
var instance;
for (var i=0; i < objects.length; i++) {
var instance = objects[i];
...
}
Svar #16
ECMAScript 5 (versionen av JavaScript) för att arbeta med arrayer:
forEach - Itererar genom varje objekt i arrayen och gör vad du än behöver med varje objekt.
['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
I så fall, mer intresserad av operationer på arrayen med hjälp av någon inbyggd funktion.
map - Det skapar en ny array med resultatet av callback-funktionen. Denna metod är bra att använda när du behöver formatera elementen i din array.
// Let's upper case the items in the array
['bob', 'joe', 'jen'].map(function(elem) {
return elem.toUpperCase();
});
// Output: ['BOB', 'JOE', 'JEN']
reduce - Som namnet säger, reducerar det arrayen till ett enda värde genom att anropa den angivna funktionen och skicka in det aktuella elementet och resultatet av föregående körning.
[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 - Returnerar sant eller falskt om alla element i arrayen klarar testet i callback-funktionen.
// 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 - Mycket likt every, förutom att filter returnerar en array med de element som returnerar sant för den givna funktionen.
// Finding the even numbers
[1,2,3,4,5,6].filter(function(elem){
return (elem % 2 == 0)
});
// Output: [2,4,6]
Svar #17
Det finns några sätt att loopa igenom en array i JavaScript, som nedan:
för - det är det vanligaste. Fullständig kodblock för loopning
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 - loop medan ett villkor är sant. Det verkar vara den snabbaste loopen
var text = "";
var i = 0;
while (i < 10) {
text += i + ") something<br>";
i++;
}
document.getElementById("example").innerHTML = text;<p id="example"></p>do/while - loopa också igenom en kodblock medan villkoret är sant, kommer att köras minst en gång
var text = ""
var i = 0;
do {
text += i + ") something <br>";
i++;
}
while (i < 10);
document.getElementById("example").innerHTML = text;<p id="example"></p>Funktionella loopar - forEach, map, filter, även reduce (de loopar genom funktionen, men de används om du behöver göra något med din array, etc.
// 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>För mer information och exempel om funktionell programmering på arrayer, titta på blogginlägget Funktionell programmering i JavaScript: map, filter och reduce.
Svar #18
Detta är en iterator för en icke-gles lista där indexet börjar på 0, vilket är det typiska scenariot när man arbetar med document.getElementsByTagName eller 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;
Exempel på användning:
Exempel #1
var arr = [];
[1, 2, 3].each( function(a){ a.push( this * this}, arr);
arr = [1, 4, 9]
Exempel #2
each.call(document.getElementsByTagName('p'), "this.className = data;",'blue');
Varje p-tag får class="blue"
Exempel #3
each.call(document.getElementsByTagName('p'),
"if( i % 2 == 0) this.className = data;",
'red'
);
Varannan p-tagg får class="red"
Exempel #4
each.call(document.querySelectorAll('p.blue'),
function(newClass, i) {
if( i < 20 )
this.className = newClass;
}, 'green'
);
Och slutligen ändras de första 20 blå p-taggarna till gröna
Var försiktig när du använder en sträng som funktion: funktionen skapas utanför sitt sammanhang och bör användas endast där du är säker på variabels räckvidd. Annars är det bättre att passera funktioner där räckvidden är mer intuitiv.
Svar #19
Använd for...of där det är möjligt
stöd för async/await | Hoppar över icke-numeriska egenskaper | Oföränderligt index | |
|---|---|---|---|
for...of | 👍 | 👍 | 👍 |
forEach() | ❌ | 👍 | 👍 |
for...in | 👍 | ❌ | 👍 |
Vanlig for | 👍 | 👍 | ❌ |
Som man kan se i tabellen ovan bör for...of användas varhelst det passar. Eftersom det stödjer asynkrona funktioner, hoppar över icke-numeriska egenskaper och förhindrar att loopen förstörs genom att man av misstag ändrar loopindexet.
Syntax
const nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const num of nums) {
/* Do something with num */
}
Se for...of-referensen för fler exempel, länk till specifikationen och skillnaden mellan for...of och for...in. Eller kanske kolla denna handledning för en förklaring om hur de skiljer sig åt.
Svar #20
Det finns ingen inbyggd möjlighet att bryta i forEach. För att avbryta körningen, använd Array#some som nedan:
[1,2,3].some(function(number) {
return number === 1;
});
Detta fungerar eftersom some returnerar true så snart någon av callbacks, som körs i array-ordning, returnerar true, vilket kortsluter exekveringen av resten. Se Array-prototypen för some för ursprungligt svar
Svar #21
Jag skulle också vilja lägga till detta som en sammansättning av en omvänd loop och ett svar ovan för någon som också skulle vilja denna syntax.
var foo = [object,object,object];
for (var i = foo.length, item; item = foo[--i];) {
console.log(item);
}
Fördelar:
Fördelen med detta: Du har referensen redan i den första raden, så den behöver inte deklareras senare med en annan rad. Det är praktiskt när man loopar igenom objektarrayen.
Nackdelar:
Detta kommer att brytas när referensen är falsk - falskaktig (undefined, etc.). Det kan dock användas som en fördel. Men det skulle göra det lite svårare att läsa. Och beroende på webbläsaren kan det också vara "inte" optimerat för att fungera snabbare än den ursprungliga.
Svar #22
jQuery-sätt med $.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];
Svar #23
Använda loopar med ECMAScript 6 destrukturering och spridningsoperatorn
Destructuring och användning av spread-operatorn har visat sig vara ganska användbart för nykomlingar till ECMAScript 6 eftersom det är mer lättläst/estetiskt, även om vissa JavaScript-veteraner kan tycka att det är stökigt. Juniorer eller vissa andra personer kan tycka att det är användbart.
Följande exempel kommer att använda
for...of-satsen och.forEach-metoden.Exempel 6, 7 och 8 kan användas med alla funktionella loopar som
.map,.filter,.reduce,.sort,.every,.some. För mer information om dessa metoder, kolla Array-objektet.
Exempel 1: Normal for...of-loop - inga tricks här.
let arrSimple = ['a', 'b', 'c'];
for (let letter of arrSimple) {
console.log(letter);
}Exempel 2: Dela ord till tecken
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);
}Exempel 3: Loopning med en key och value
// 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);
}Exempel 4: Hämta objektets egenskaper direkt
let arrWithObjects = [{
name: 'Jon',
age: 32
},
{
name: 'Elise',
age: 33
}
];
for (let { name, age: aliasForAge } of arrWithObjects) {
console.log(name, aliasForAge);
}Exempel 5: Hämta djupa objektsegenskaper av det du behöver
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);
}Exempel 6: Används Exempel 3 med .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);
});Exempel 7: Används Exempel 4 med .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)
});Exempel 8: Används Exempel 5 med .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);
});Svar #24
Sammanfattning :
När man itererar över en array vill man ofta uppnå ett av följande mål:
Vi vill iterera över arrayen och skapa en ny array:
Array.prototype.mapVi vill iterera över arrayen och skapa inte en ny array:
Array.prototype.forEach
for..ofloop
I JavaScript finns det många sätt att uppnå båda dessa mål. Några är dock mer bekväma än andra. Nedan kan du hitta några ofta använda metoder (de mest bekväma enligt mig) för att genomföra array-iteration i JavaScript.
Skapar ny array: Map
map() är en funktion som finns på Array.prototype och som kan omvandla varje element i en array och sedan returnerar en ny array. map() tar som argument en callbackfunktion och fungerar på följande sätt:
let arr = [1, 2, 3, 4, 5];
let newArr = arr.map((element, index, array) => {
return element * 2;
})
console.log(arr);
console.log(newArr);Den callback som vi har skickat in i map() som ett argument körs för varje element. Sedan returneras en array som har samma längd som den ursprungliga arrayen. I denna nya array omvandlas varje element av callback-funktionen som skickades in som ett argument till map().
Den tydliga skillnaden mellan map och en annan loop-mekanism som forEach och en for..of-loop är att map returnerar en ny array och lämnar den gamla arrayen intakt (förutom om du uttryckligen manipulerar den med saker som splice).
Observera också att map-funktionens callback tillhandahåller indexnumret för den aktuella iterationen som ett andra argument. Dessutom, tillhandahåller det tredje argumentet arrayen på vilken map kallades? Ibland kan dessa egenskaper vara mycket användbara.
Loopa med forEach
forEach är en funktion som finns på Array.prototype och tar en callback-funktion som ett argument. Den kör sedan denna callback-funktion för varje element i arrayen. I motsats till map()-funktionen returnerar forEach-funktionen ingenting (undefined). Till exempel:
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);Precis som map-funktionen, ger forEach-callbacken indexnumret för den aktuella iterationen som ett andra argument. Ger det tredje argumentet också arrayen som forEach kallades på?
Loopa genom element med for..of
for..of-loopen loopar igenom varje element i en array (eller något annat itererbart objekt). Den fungerar på följande sätt:
let arr = [1, 2, 3, 4, 5];
for(let element of arr) {
console.log(element * 2);
}I exemplet ovan står element för ett arrayelement och arr är arrayen som vi vill loopa igenom. Observera att namnet element är godtyckligt, och vi skulle kunna ha valt ett annat namn som 'el' eller något mer beskrivande när detta är tillämpligt.
Förväxla inte for..in-loopen med for..of-loopen. for..in kommer att loopa igenom alla räkneliga egenskaper i arrayen, medan for..of-loopen endast kommer att loopa igenom arrayens element. Till exempel:
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);
}Svar #25
Prestanda
Idag (2019-12-18) utför jag tester på min macOS v10.13.6 (High Sierra), på Chrome v79.0, Safari v13.0.4 och Firefox v71.0 (64-bit) - slutsatser om optimering (och mikro-optimering som vanligtvis inte är värd att införa i koden eftersom vinsten är liten, men kodkomplexiteten ökar).
Det ser ut som att den traditionella
for i(Aa) är ett bra val för att skriva snabb kod i alla webbläsare.De andra lösningarna, som
for-of(Ad), alla i grupp C.... är vanligtvis 2 - 10 (och fler) gånger långsammare än Aa, men för små arrayer är det okej att använda det - för att öka kodens tydlighet.Looparna med arraylängden cachelagrad i
n(Ab, Bb, Be) är ibland snabbare, ibland inte. Troligtvis upptäcker kompilatorer automatiskt denna situation och inför caching. Hastighetsskillnaderna mellan de cachelagrade och icke-cachelagrade versionerna (Aa, Ba, Bd) är ungefär ~1%, så det verkar som att införanär en mikro-optimering.Lösningar med
i--där loopen startar från den sista array-elementet (Ac, Bc) är vanligtvis ~30% långsammare än framåtriktade lösningar - troligen beror det på hur CPU:ns minnescache fungerar - framåtläsning av minne är mer optimal för CPU-caching). Det rekommenderas ATT INTE ANVÄNDA sådana lösningar.
Detaljer
I tester beräknar vi summan av arrayelement. Jag utför ett test för små arrayer (10 element) och stora arrayer (1 miljon element) och delar in dem i tre grupper:
- A -
förtester - B -
while-tester - C - andra/alternativa metoder
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
//let arr = Array.from(Array(1000000), (x, i) => i%10);
function Aa(a, s=0) {
for(let i=0; i<a.length; i++) {
s += a[i];
}
console.log('Aa=', s);
}
function Ab(a, s=0) {
let n = a.length;
for(let i=0; i<n; i++) {
s += a[i];
}
console.log('Ab=', s);
}
function Ac(a, s=0) {
for(let i=a.length; i--;) {
s += a[i];
}
console.log('Ac=', s);
}
function Ad(a, s=0) {
for(let x of a) {
s += x;
}
console.log('Ad=', s);
}
function Ae(a, s=0) {
for(let i in a) if (a.hasOwnProperty(i)) {
s += a[i];
}
console.log('Ae=', s);
}
function Ba(a, s=0) {
let i = -1;
while(++i < a.length) {
s+= a[i];
}
console.log('Ba=', s);
}
function Bb(a, s=0) {
let i = -1;
let n = a.length;
while(++i < n) {
s+= a[i];
}
console.log('Bb=', s);
}
function Bc(a, s=0) {
let i = a.length;
while(i--) {
s += a[i];
}
console.log('Bc=', s);
}
function Bd(a, s=0) {
let i = 0;
do {
s+= a[i]
} while (++i < a.length);
console.log('Bd=', s);
}
function Be(a, s=0) {
let i = 0;
let n = a.length;
do {
s += a[i]
} while (++i < n);
console.log('Be=', s);
}
function Bf(a, s=0) {
const it = a.values();
let e;
while (!(e = it.next()).done) {
s+= e.value;
}
console.log('Bf=', s);
}
function Ca(a, s=0) {
a.map(x => { s+=x });
console.log('Ca=', s);
}
function Cb(a, s=0) {
a.forEach(x => { s+=x });
console.log('Cb=', s);
}
function Cc(a, s=0) {
a.every(x => (s += x, 1));
console.log('Cc=', s);
}
function Cd(a, s=0) {
a.filter(x => { s+=x });
console.log('Cd=',s);
}
function Ce(a, s=0) {
a.reduce((z, c) => { s+=c }, 0);
console.log('Ce=', s);
}
function Cf(a, s=0) {
a.reduceRight((z, c) => { s += c }, 0);
console.log('Cf=', s);
}
function Cg(a, s=0) {
a.some(x => { s += x } );
console.log('Cg=', s);
}
function Ch(a, s=0) {
Array.from(a, x=> s += x);
console.log('Cc=', s);
}
Aa(arr);
Ab(arr);
Ac(arr);
Ad(arr);
Ae(arr);
Ba(arr);
Bb(arr);
Bc(arr);
Bd(arr);
Be(arr);
Bf(arr);
Ca(arr);
Cb(arr);
Cc(arr);
Cd(arr);
Ce(arr);
Cf(arr);
Cg(arr);
Ch(arr);<p style="color: red">This snippets only PRESENTS code used for benchmark - it not perform test itself</p>Resultat i olika webbläsare
Resultat för alla testade webbläsare
Array med 10 element
Resultat för Chrome. Du kan utföra testet på din dator här.
Array med 1 000 000 element
Resultat för Chrome. Du kan utföra testet på din dator här


