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
forEach
ve ilgili (ES5 +)
- Basit bir
for
dö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 forEach
ve ilgili
Array
ES5'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);
});
forEach
bir geri arama işlevini ve isteğe bağlı olarak, this
bu 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 forEach
kolayca 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 forEach
olarak, "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 false
veya sahte bir şey yapmayı durdurur )
some
(geri arama ilk kez döndüğünde true
veya gerçeğe uygun bir şey yapmayı durdurur)
filter
(filtre işlevinin geri döndüğü öğeleri içeren true
ve 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 for
dö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 for
dö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, value
aynı zamanda index
yeniden 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 var
yerine let
.
3. Doğru kullanınfor-in
Kullanmak söylüyorum insanları alırsınız for-in
, ama bu değil ne for-in
içindir . for-in
dö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-in
bu emri takip edecek; Yine de ES2020 yaptı. ( Bu diğer cevaptaki ayrıntılar .)
for-in
Bir 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-in
Uygun ö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-of
ifadedir. Şö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-in
Nesne (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-in
ES5'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 next
yöntemi, yeni bir döndürür sonuç nesnesi diyorsunuz her seferinde. Sonuç nesnesi, done
bize yapılıp yapılmadığını söyleyen bir özelliğe value
ve bu yinelemenin değerine sahip bir özelliğe sahiptir. ( done
olması isteğe bağlıdır false
, value
olması isteğe bağlıdır undefined
.)
Anlamı value
yineleyiciye 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 value
bu yineleme için dizi giriştir ( "a"
, "b"
ve "c"
örneğin daha önce).
keys()
: Her birinin value
o yinelemenin anahtarı olduğu bir yineleyici döndürür (yani, a
yukarıdaki için, o "0"
zaman "1"
, o zaman "2"
).
entries()
: Her birinin o yineleme value
iç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, arguments
nesne, 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 forEach
ve ilgili (ES5 +)
Üzerindeki çeşitli işlevler Array.prototype
"kasıtlı olarak genel" dir ve genellikle Function#call
veya 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 forEach
bir üzerinde Node
bireyin childNodes
mü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 for
döngü kullanın
Açıkçası, basit bir for
döngü dizi benzeri nesneler için geçerlidir.
Doğru kullanınfor-in
for-in
bir 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-of
kullanan yineleyici nesnesi (eğer varsa) ile yapılmalıdır. Ana bilgisayar tarafından sağlanan nesneleri içerir. Örneğin, NodeList
from için teknik özellik querySelectorAll
yinelemeyi destekleyecek şekilde güncellendi. HTMLCollection
Kimden için spec getElementsByTagName
değ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:
slice
Diziler yöntemini kullanın
slice
Yukarı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ı NodeList
gerç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ı NodeList
gerç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.prototype
ile 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.prototype
kullanmak 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.)
forEach
ve sadece değilfor
. belirtildiği gibi, c # biraz farklıydı ve bu beni