TL; DR
Ama keşfedilecek çok şey var , okumaya devam edin ...
JavaScript, diziler ve dizi benzeri nesneler arasında döngü yapmak için güçlü semantiğe sahiptir. Cevabı iki bölüme ayırdım : Orijinal diziler için seçenekler ve nesne, diğer yinelenebilir nesneler (ES2015 +), DOM koleksiyonları ve benzeri gibi sadece dizi benzeri şeyler için seçenekler arguments.
ES2015'i ES5'e aktararak , ES5 motorlarında bile şimdi ES2015 seçeneklerini kullanabileceğinizi hemen anlayacağım. Daha fazla bilgi için "ES2015 aktarma" / "ES6 aktarma" için arama ...
Tamam, seçeneklerimize bakalım:
Gerçek Diziler İçin
ECMAScript 5'te ("ES5") üç seçenek vardır , şu anda en yaygın olarak desteklenen sürüm ve ECMAScript 2015'e eklenmiş iki sürüm daha ("ES2015", "ES6"):
- Kullanım
forEachve ilgili (ES5 +)
- Basit bir
fordöngü kullanın
- Doğru kullanın
for-in
- Kullan
for-of(üstü kapalı bir yineleyici kullan ) (ES2015 +)
- Açıkça yineleyici kullanma (ES2015 +)
Detaylar:
1. Kullanım forEachve ilgili
ArrayES5'in eklediği özelliklere (doğrudan veya çoklu dolgular kullanarak) erişebileceğiniz herhangi bir belirsiz modern ortamda (IE8 değil ), forEach( spec| MDN) kullanabilirsiniz :
var a = ["a", "b", "c"];
a.forEach(function(entry) {
console.log(entry);
});
forEachbir geri arama işlevini ve isteğe bağlı olarak, thisbu geri aramayı çağırırken kullanılacak bir değeri (yukarıda kullanılmaz) kabul eder. Seyir dizilerinde var olmayan girişleri atlamak için dizideki her giriş için geri arama çağrılır. Yukarıda yalnızca bir bağımsız değişken kullanmış olmama rağmen, geri çağrı üç ile çağrılır: Her girişin değeri, o girişin dizini ve yinelemekte olduğunuz diziye bir başvuru (işlevinizin zaten kullanışlı olmaması durumunda) ).
IE8 gibi eski tarayıcıları desteklemediğiniz sürece (NetApps Eylül 2016'da bu yazının% 4'ünden biraz fazla pazar payında gösterilir), forEachşim olmadan genel amaçlı bir web sayfasında mutlu bir şekilde kullanabilirsiniz . Eski tarayıcıları desteklemeniz gerekiyorsa, shimming / polyfilling forEachkolayca yapılabilir (çeşitli seçenekler için "es5 shim" araması yapın).
forEach , yineleme işlevine bağımsız değişkenler olarak sağlandıkları ve yalnızca bu yinelemenin kapsamı dahilinde olduğu için, dizin oluşturma ve değer değişkenlerini içeren kapsamda bildirmeniz gerekmemesi avantajına sahiptir.
Her dizi girişi için bir işlev çağrısı yapmanın çalışma zamanı maliyetinden endişe ediyorsanız, olmayın; ayrıntılar .
Buna ek forEacholarak, "hepsi arasında döngü" işlevi, ancak ES5 diğer birkaç yararlı "dizi boyunca yol ve bir şeyler yapmak" tanımlanmış tanımlanmıştır:
every(geri arama ilk kez döndüğünde falseveya sahte bir şey yapmayı durdurur )
some(geri arama ilk kez döndüğünde trueveya gerçeğe uygun bir şey yapmayı durdurur)
filter(filtre işlevinin geri döndüğü öğeleri içeren trueve geri döndüklerini içeren öğeleri içeren yeni bir dizi oluşturur false)
map (geri arama tarafından döndürülen değerlerden yeni bir dizi oluşturur)
reduce (önceki değerleri ileterek geri aramayı tekrar tekrar arayarak bir değer oluşturur; ayrıntılar için spesifikasyona bakın; bir dizinin içeriğini ve diğer birçok şeyi toplamak için yararlıdır)
reduceRight(gibi reduce, artan düzende değil azalan düzende çalışır)
2. Basit bir fordöngü kullanın
Bazen eski yollar en iyisidir:
var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
console.log(a[index]);
}
Dizinin uzunluğu döngü sırasında değişmeyecek ve (olası) performans duyarlı kodunda ise, ön uzunluğunu kapma biraz daha karmaşık sürümü olabilir küçücük biraz daha hızlı:
var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
console.log(a[index]);
}
Ve / veya geriye doğru saymak:
var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
console.log(a[index]);
}
Ancak modern JavaScript motorlarında, son meyve suyunu eklemeniz nadirdir.
ES2015 ve daha yeni sürümlerde, dizin ve değer değişkenlerinizi fordöngü için yerel yapabilirsiniz :
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"
}
Bunu yaptığınızda, her döngü yinelemesi için değil, valueaynı zamanda indexyeniden oluşturulduğunda, döngü gövdesinde oluşturulan kapanmalar , söz konusu yineleme için oluşturulan index(ve value) referansı tutar :
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>
Beş bölümünüz olsaydı, ilkini tıklarsanız "Dizin: 0" ve sonuncuyu tıkladıysanız "Dizin: 4" alırsınız. Bu mu değil kullanırsanız çalışmak varyerine let.
3. Doğru kullanınfor-in
Kullanmak söylüyorum insanları alırsınız for-in, ama bu değil ne for-iniçindir . for-indöngüsü bir nesnenin numaralandırılabilir özellikleri , bir dizi olup dizinler. Sipariş ES2015'te (ES6) bile garanti edilmez. ES2015 + nesne özelliklerinde bir emir tanımlamaz (aracılığıyla [[OwnPropertyKeys]], [[Enumerate]]ve bunları kullanmak şeyler gibi Object.getOwnPropertyKeys), ama o kadar tanımlamak vermedi for-inbu emri takip edecek; Yine de ES2020 yaptı. ( Bu diğer cevaptaki ayrıntılar .)
for-inBir dizideki gerçek kullanım durumları şunlardır:
- Bu bir var seyrek diziler ile masif , içinde boşluklar veya
- Eleman olmayan özellikleri kullanıyorsunuz ve bunları döngüye dahil etmek istiyorsunuz
Yalnızca ilk örneğe baktığımızda: for-inUygun önlemleri kullanıyorsanız, seyrek dizi öğelerini ziyaret etmek için kullanabilirsiniz:
// `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 <= 4294967294 // below
) {
console.log(a[key]);
}
}
Üç kontrole dikkat edin:
Nesnenin bu ada göre kendi özelliğine sahip olması (prototipinden miras aldığı değil) ve
Anahtarın tüm ondalık basamaklar olması (ör. Bilimsel gösterim değil normal dize formu) ve
Bir sayıya zorlandığında anahtarın değeri <= 2 ^ 32 - 2 (4,294,967,294). Bu sayı nereden geliyor? Bu , belirtimdeki bir dizi dizini tanımının bir parçasıdır . Diğer sayılar (tamsayı olmayanlar, negatif sayılar, 2 ^ 32 - 2'den büyük sayılar) dizi indeksleri değildir. Bunun 2 ^ 32 - 2 olmasının nedeni, en büyük dizin değerini , bir dizinin sahip olabileceği maksimum değer olan 2 ^ 32 - 1'den daha düşük hale getirmesidir length. (Örneğin, bir dizinin uzunluğu 32 bit işaretsiz bir tam sayıya sığar.) (Önceki yazımın tam olarak doğru olmadığını blog blogumdaki bir yorumda işaret ettiği için RobG'ye destek .)
Elbette bunu satır içi kodda yapmazsınız. Bir yardımcı program işlevi yazarsınız. Belki:
// Utility function for antiquated environments without `forEach`
var hasOwn = Object.prototype.hasOwnProperty;
var rexNum = /^0$|^[1-9]\d*$/;
function sparseEach(array, callback, thisArg) {
var index;
for (var key in array) {
index = +key;
if (hasOwn.call(a, key) &&
rexNum.test(key) &&
index <= 4294967294
) {
callback.call(thisArg, array[key], index, array);
}
}
}
var a = [];
a[5] = "five";
a[10] = "ten";
a[100000] = "one hundred thousand";
a.b = "bee";
sparseEach(a, function(value, index) {
console.log("Value at " + index + " is " + value);
});
4. Kullanım for-of(dolaylı olarak yineleyici kullanın ) (ES2015 +)
ES2015 , JavaScript'e yineleyiciler ekledi . Yineleyicileri kullanmanın en kolay yolu yeni for-ofifadedir. Şöyle görünüyor:
const a = ["a", "b", "c"];
for (const val of a) {
console.log(val);
}
Kapakların altında, diziden bir yineleyici alır ve içinden döngüler alır , değerleri alır. for-inNesne (dizi) tarafından tanımlanan bir yineleyici kullandığından ve diziler, yineleyicilerinin girdileri (özellikleri değil) aracılığıyla yinelediğini tanımladığı için, kullanımın sahip olduğu bir sorun yoktur. for-inES5'ten farklı olarak, girişlerin ziyaret edilme sırası dizinlerinin sayısal sırasıdır.
5. Açıkça bir yineleyici kullanın (ES2015 +)
Bazen, açıkça bir yineleyici kullanmak isteyebilirsiniz . Bunu da yapabilirsiniz, ancak çok daha karmaşıktır . Şöyle görünüyor:for-of
const a = ["a", "b", "c"];
const it = a.values();
let entry;
while (!(entry = it.next()).done) {
console.log(entry.value);
}
Yineleyici, belirtimdeki Yineleyici tanımıyla eşleşen bir nesnedir. Onun nextyöntemi, yeni bir döndürür sonuç nesnesi diyorsunuz her seferinde. Sonuç nesnesi, donebize yapılıp yapılmadığını söyleyen bir özelliğe valueve bu yinelemenin değerine sahip bir özelliğe sahiptir. ( doneolması isteğe bağlıdır false, valueolması isteğe bağlıdır undefined.)
Anlamı valueyineleyiciye bağlı olarak değişir; diziler yineleyicileri döndüren (en azından) üç işlevi destekler:
values(): Bu benim yukarıda kullandığım. Her bir yineleyici döndüren valuebu yineleme için dizi giriştir ( "a", "b"ve "c"örneğin daha önce).
keys(): Her birinin valueo yinelemenin anahtarı olduğu bir yineleyici döndürür (yani, ayukarıdaki için, o "0"zaman "1", o zaman "2").
entries(): Her birinin o yineleme valueiçin formdaki bir dizi olduğu bir yineleyici döndürür [key, value].
Dizi Benzeri Nesneler İçin
Gerçek dizilerin yanı sıra, sayısal adlara sahip bir özelliğe ve özelliklere sahip dizi benzeri nesneler de vardır length: NodeListörnekler, argumentsnesne, vb. İçerikleri arasında nasıl döngü oluştururuz?
Diziler için yukarıdaki seçeneklerden herhangi birini kullanın
Yukarıdaki dizi yaklaşımlarının en azından bazıları ve hatta çoğu veya hatta tümü, dizi benzeri nesnelere sıklıkla eşit derecede iyi uygulanır:
Kullanım forEachve ilgili (ES5 +)
Üzerindeki çeşitli işlevler Array.prototype"kasıtlı olarak genel" dir ve genellikle Function#callveya benzeri diziler gibi nesnelerde kullanılabilir Function#apply. ( Bu cevabın sonunda ana bilgisayar tarafından sağlanan nesneler için Caveat'a bakın , ancak bu nadir bir sorundur.)
Kullanmak istediğim varsayalım forEachbir üzerinde Nodebireyin childNodesmülkiyet. Bunu yapardınız:
Array.prototype.forEach.call(node.childNodes, function(child) {
// Do something with `child`
});
Bunu çok yapacaksanız, işlev başvurusunun bir kopyasını yeniden kullanım için bir değişkene almak isteyebilirsiniz, örneğin:
// (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`
});
Basit bir fordöngü kullanın
Açıkçası, basit bir fordöngü dizi benzeri nesneler için geçerlidir.
Doğru kullanınfor-in
for-inbir dizi ile aynı güvenlik önlemleri ile dizi benzeri nesnelerle de çalışmalıdır; yukarıdaki # 1 üzerindeki ana bilgisayar tarafından sağlanan nesneler için uyarı geçerli olabilir.
Kullan for-of(üstü kapalı bir yineleyici kullan ) (ES2015 +)
for-ofkullanan yineleyici nesnesi (eğer varsa) ile yapılmalıdır. Ana bilgisayar tarafından sağlanan nesneleri içerir. Örneğin, NodeListfrom için teknik özellik querySelectorAllyinelemeyi destekleyecek şekilde güncellendi. HTMLCollectionKimden için spec getElementsByTagNamedeğildi.
Açıkça yineleyici kullanma (ES2015 +)
Bkz.
Gerçek bir dizi oluşturun
Diğer zamanlarda, dizi benzeri bir nesneyi gerçek bir diziye dönüştürmek isteyebilirsiniz. Bunu yapmak şaşırtıcı derecede kolaydır:
sliceDiziler yöntemini kullanın
sliceYukarıda belirtilen diğer yöntemler gibi "kasıtlı olarak genel" olan diziler yöntemini kullanabiliriz ve bu şekilde dizi benzeri nesnelerle kullanılabilir:
var trueArray = Array.prototype.slice.call(arrayLikeObject);
Örneğin, a'yı NodeListgerçek bir diziye dönüştürmek istiyorsak , bunu yapabiliriz:
var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
Bkz konak tarafından sağlanan nesneler için ikaz altında. Özellikle, bunun IE8 ve önceki sürümlerinde başarısız olacağını ve bu şekilde ana bilgisayar tarafından sağlanan nesneleri kullanmanıza izin vermediğini unutmayın this.
Forma sözdizimini kullan ( ...)
ES2015'in yayılma sözdizimini bu özelliği destekleyen JavaScript motorlarıyla da kullanmak mümkündür . Gibi for-of, bu kullanır yineleyici nesnesi tarafından sağlanan (önceki bölümde 4. bakınız):
var trueArray = [...iterableObject];
Örneğin, a'yı NodeListgerçek bir diziye dönüştürmek istiyorsak , yayılım sözdizimi ile bu oldukça kısa ve öz olur:
var divs = [...document.querySelectorAll("div")];
kullanım Array.from
Array.from (spec) | (MDN) (ES2015 +, ancak kolayca çok katmanlı), dizi benzeri bir nesneden bir dizi oluşturur ve isteğe bağlı olarak ilk önce girdileri bir eşleme işlevinden geçirir. Yani:
var divs = Array.from(document.querySelectorAll("div"));
Veya belirli bir sınıfa sahip öğelerin etiket adlarından oluşan bir dizi almak istiyorsanız, eşleme işlevini kullanırsınız:
// 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;
});
Ana bilgisayar tarafından sağlanan nesneler için uyarı
Eğer kullanırsanız Array.prototypeile işlevlerini konak tarafından sağlanan dizi benzeri (ziyade JavaScript motoru daha tarayıcı tarafından sağlanan DOM listeleri ve diğer şeyler) nesneler, emin konak tarafından sağlanan nesne davranır düzgün hale getirmek için hedef ortamlarında teste emin olmak gerekir . Çoğu (şimdi) düzgün davranıyor , ancak test etmek önemlidir. Bunun nedeni, Array.prototypekullanmak isteyeceğiniz yöntemlerin çoğunun , soyut [[HasProperty]]işlem için dürüst bir cevap veren ana bilgisayar tarafından sağlanan nesneye dayanmasıdır . Bu yazıdan itibaren, tarayıcılar bu konuda çok iyi bir iş çıkarıyor, ancak 5.1 spesifikasyonu, ana bilgisayar tarafından sağlanan bir nesnenin dürüst olmayabilir. Bu öyle §8.6.2 , diyor o bölümün başında), yakın büyük tablonun altındaki birkaç paragraflar:
Ana bilgisayar nesneleri, aksi belirtilmedikçe bu dahili yöntemleri herhangi bir şekilde uygulayabilir; örneğin, bir ihtimal olduğunu [[Get]]ve [[Put]]belirli bir konakçı nesnesi gerçekten de getirme ve mağaza özellik değerleri için değil, [[HasProperty]]hep oluşturur yanlış .
(Ben ES2015 spec eşdeğer şişirme bulamadık, ama yine de durum olacagi kesin.) Yine, ortak konak tarafından sağlanan [modern tarayıcılarda nesnelerin dizi benzeri bu yazının yazıldığı sırada NodeList, örneğin örnekleri] do sapı [[HasProperty]]doğru, ancak test etmek önemlidir.)
forEachve sadece değilfor. belirtildiği gibi, c # biraz farklıydı ve bu beni