JavaScript kullanarak bir dizideki tüm girdiler arasında nasıl döngü yapabilirim?
Bunun gibi bir şey olduğunu düşünmüştüm:
forEach(instance in theArray)
Burada theArray
benim dizimdir, ancak bu yanlış gibi görünüyor.
TL;DR
for-of
döngüsü (yalnızca ES2015+),Array#forEach
(spec
| MDN
) (veya some
ve benzeri akrabaları) (sadece ES5+),for
döngüsü,JavaScript, diziler ve dizi benzeri nesneler arasında döngü oluşturmak için güçlü anlambilimlere sahiptir. Cevabı iki bölüme ayırdım: Gerçek diziler için seçenekler ve arguments
nesnesi, diğer yinelenebilir nesneler (ES2015+), DOM koleksiyonları vb. gibi sadece dizi benzeri şeyler için seçenekler.
ES2015 seçeneklerini şimdi ES5 motorlarında bile ES2015'ten ES5'e transpiling yaparak kullanabileceğinizi hemen belirteyim. Daha fazlası için "ES2015 transpiling" / "ES6 transpiling" araması yapın...
Tamam, seçeneklerimize bakalım:
Şu anda en geniş şekilde desteklenen sürüm olan ECMAScript 5 ("ES5")'te üç seçeneğiniz vardır ve ECMAScript 2015 ("ES2015", "ES6")'te iki tane daha eklenmiştir:
for
döngüsü kullanınES5 tarafından eklenen Array
özelliklerine erişiminizin olduğu (doğrudan veya polyfills kullanarak) herhangi bir belirsiz modern ortamda (yani IE8 değil), forEach
(spec
| MDN
) kullanabilirsiniz:
var a = ["a", "b", "c"];
a.forEach(function(entry) {
console.log(entry);
});
forEachbir geri çağırma fonksiyonu ve isteğe bağlı olarak bu geri çağırmayı çağırırken
thisolarak kullanılacak bir değer kabul eder (yukarıda kullanılmamıştır). Geriçağırım, seyrek dizilerde var olmayan girdileri atlayarak, dizideki her girdi için sırayla çağrılır. Yukarıda sadece bir argüman kullanmış olmama rağmen, geri çağrı üç argümanla çağrılır: Her bir girdinin değeri, o girdinin indeksi ve üzerinde yineleme yaptığınız diziye bir referans (fonksiyonunuzda zaten yoksa). IE8 gibi eski tarayıcıları desteklemediğiniz sürece (NetApps bu yazının yazıldığı Eylül 2016 itibariyle pazar payının %4'ün biraz üzerinde olduğunu gösteriyor),
forEach'i genel amaçlı bir web sayfasında şim olmadan rahatlıkla kullanabilirsiniz. Eski tarayıcıları desteklemeniz gerekiyorsa, forEach
için shimming/polyfilling kolayca yapılabilir (çeşitli seçenekler için "es5 shim" araması yapın).
forEach, yineleme işlevine argüman olarak sağlandıkları ve sadece o yineleme için güzel bir şekilde kapsamlandırıldıkları için, dizinleme ve değer değişkenlerini içeren kapsamda bildirmek zorunda kalmamanız gibi bir avantaja sahiptir. Her dizi girişi için bir fonksiyon çağrısı yapmanın çalışma zamanı maliyeti konusunda endişeleniyorsanız, endişelenmeyin; [details](http://blog.niftysnippets.org/2012/02/foreach-and-runtime-cost.html). Ek olarak,
forEach' "hepsinde döngü" işlevidir, ancak ES5, aşağıdakiler de dahil olmak üzere diğer birçok yararlı "dizi boyunca yolunuzu çalışın ve bir şeyler yapın" işlevini tanımlamıştır:
every
(geri çağırma false
ya da başka bir şey döndürdüğünde döngüyü durdurur)some
(geri arama ilk kez true
veya doğru bir şey döndürdüğünde döngüyü durdurur)filter
(filtre işlevinin true
döndürdüğü öğeleri içeren ve false
döndürdüklerini çıkaran yeni bir dizi oluşturur)map
(geri arama tarafından döndürülen değerlerden yeni bir dizi oluşturur)reduce
(önceki değerleri aktararak geri çağrıyı tekrar tekrar çağırarak 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 kullanışlıdır)reduceRight
(reduce
gibi, ancak artan yerine azalan sırada çalışır)for
döngüsü kullanınBazen eski yöntemler 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şmeyecekse ve performansa duyarlı bir koddaysa (pek olası değil), uzunluğu önceden yakalayan biraz daha karmaşık bir sürüm tiny biraz daha hızlı olabilir:
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 sayma:
var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
console.log(a[index]);
}
Ancak modern JavaScript motorlarıyla, son bir parça güç elde etmeniz nadiren gerekir.
ES2015 ve daha yüksek sürümlerde, indeks ve değer değişkenlerinizi for
döngüsü 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 yalnızca value
değil, index
de yeniden oluşturulur, yani döngü gövdesinde oluşturulan kapanışlar, o belirli yineleme için oluşturulan index
e (ve value
ya) bir 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>
Eğer beş div'iniz olsaydı, "Index is: 0" ve eğer ilkine tıklarsanız "Index is: 4" eğer sonuncuya tıklarsanız. Eğer let' yerine
var' kullanırsanız bu çalışmaz.
İnsanlar size for-in' kullanmanızı söyleyecektir, ancak
for-in' bunun için değildir]11. for-inbir nesnenin *sayılabilir özellikleri* üzerinde döngü yapar, bir dizinin indeksleri üzerinde değil. ES2015'te (ES6) bile **sıra garanti edilmez**. ES2015+ nesne özellikleri için bir sıra tanımlar ([
[[OwnPropertyKeys]]](https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys), [
[[Enumerate]]](https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-enumerate) ve [
Object.getOwnPropertyKeys][12] gibi bunları kullanan şeyler aracılığıyla), ancak
for-inin bu sırayı izleyeceğini **tanımlamaz**. (Ayrıntılar [bu diğer yanıtta][13].) Bir dizi üzerinde
for-in` için tek gerçek kullanım durumları şunlardır:
for-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:
// 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);
});
ES2015 JavaScript'e iteratörler ekler. Yineleyicileri kullanmanın en kolay yolu yeni for-of
deyimidir. Şöyle görünür:
const a = ["a", "b", "c"];
for (const val of a) {
console.log(val);
}
Gizli olarak, bu dizi içinden bir iterator alır ve içinden değerleri alarak döngüye sokar. Bu, for-in' kullanımının sahip olduğu soruna sahip değildir, çünkü nesne (dizi) tarafından tanımlanan bir yineleyici kullanır ve diziler, yineleyicilerinin * girdileri * (özellikleri değil) aracılığıyla yinelendiğini tanımlar. ES5
teki for-in
den farklı olarak, girdilerin ziyaret edilme sırası indekslerinin sayısal sırasıdır.
Bazen bir yineleyiciyi açıkça kullanmak isteyebilirsiniz. Bunu da yapabilirsiniz, ancak for-of
dan çok daha hantaldır. Şuna benzer:
const a = ["a", "b", "c"];
const it = a.values();
let entry;
while (!(entry = it.next()).done) {
console.log(entry.value);
}
Yineleyici, belirtimdeki Iterator tanımına uyan bir nesnedir. Onun next
metodu her çağırışınızda yeni bir sonuç nesnesi döndürür. Sonuç nesnesi, bize bitip bitmediğini söyleyen bir done
özelliğine ve o yinelemenin değerini içeren bir value
özelliğine sahiptir. (Eğer false
olacaksa done
isteğe bağlıdır, undefined
olacaksa value
isteğe bağlıdır).
değer`in anlamı yineleyiciye bağlı olarak değişir; diziler (en azından) yineleyici döndüren üç işlevi destekler:
values()
: Bu yukarıda kullandığımdır. Her değer
in o yineleme için dizi girdisi olduğu bir yineleyici döndürür (önceki örnekte "a"
, "b"
ve "c"
).keys()
: Her value
nun o yinelemenin anahtarı olduğu bir yineleyici döndürür (yani yukarıdaki a
için bu "0"
, sonra "1"
, sonra "2"
olacaktır).entries()
: Her değer
in o yineleme için [anahtar, değer]
biçiminde bir dizi olduğu bir yineleyici döndürür.Gerçek dizilerin yanı sıra, length
özelliğine ve sayısal adlara sahip özelliklere sahip dizi benzeri nesneler de vardır: NodeList
örnekleri, arguments
nesnesi, vb. Bunların içerikleri arasında nasıl döngü yaparız?
Yukarıdaki dizi yaklaşımlarının en azından bir kısmı ve muhtemelen çoğu ya da tamamı dizi benzeri nesneler için de sıklıkla aynı derecede geçerlidir:
forEach` ve benzerlerini kullanın (ES5+)
Array.prototypeüzerindeki çeşitli fonksiyonlar "kasıtlı olarak geneldir" ve genellikle [
Function#call][15] veya [
Function#apply][16] aracılığıyla dizi benzeri nesneler üzerinde kullanılabilir. (Bu yanıtın sonundaki *Ana bilgisayar tarafından sağlanan nesneler için mağara* bölümüne bakın, ancak bu nadir bir konudur). Bir
Node'un
childNodesözelliği üzerinde
forEachkullanmak istediğinizi varsayalım. Bunu yaparsınız: Array.prototype.forEach.call(node.childNodes, function(child) { //
childile bir şeyler yapın }); Eğer bunu çok sık yapacaksanız, fonksiyon referansının bir kopyasını tekrar kullanmak için bir değişkene almak isteyebilirsiniz, örn: // (Bunların hepsi muhtemelen bazı kapsam belirleme işlevlerinde) var forEach = Array.prototype.forEach; // Sonra... forEach.call(node.childNodes, function(child) { //
child` ile bir şeyler yapın
});
Basit bir for
döngüsü kullanın
Açıkçası, dizi benzeri nesneler için basit bir for
döngüsü geçerlidir.
for-in` doğru şekilde kullanın Bir dizi ile aynı korumalara sahip `for-in', dizi benzeri nesnelerle de çalışmalıdır; yukarıdaki #1'deki ana bilgisayar tarafından sağlanan nesneler için uyarı geçerli olabilir.
for-of` kullanın (dolaylı olarak bir yineleyici kullanın) (ES2015+)
for-ofnesne tarafından sağlanan yineleyiciyi kullanacaktır (eğer varsa); bunun çeşitli dizi benzeri nesnelerle, özellikle de ana bilgisayar tarafından sağlananlarla nasıl çalıştığını görmemiz gerekecek. Örneğin,
querySelectorAlldan
NodeListiçin spesifikasyon yinelemeyi destekleyecek şekilde güncellendi. GetElementsByTagName
den gelen HTMLCollection
için spesifikasyon güncellenmedi.
Açıkça bir yineleyici kullanın (ES2015+) 4'e bakın, yineleyicilerin nasıl çalışacağını görmemiz gerekecek.
Diğer zamanlarda, dizi benzeri bir nesneyi gerçek bir diziye dönüştürmek isteyebilirsiniz. Bunu yapmak şaşırtıcı derecede kolaydır:
Dizilerin slice
yöntemini kullanın
Yukarıda bahsedilen diğer yöntemler gibi "intentionally generic" olan ve bu nedenle bunun gibi dizi benzeri nesnelerle kullanılabilen slice
yöntemini kullanabiliriz:
var trueArray = Array.prototype.slice.call(arrayLikeObject);
Örneğin, bir NodeList
i gerçek bir diziye dönüştürmek istiyorsak, bunu yapabiliriz:
var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
Aşağıdaki ana bilgisayar tarafından sağlanan nesneler için Caveat bölümüne bakın. Özellikle, bunun ana bilgisayar tarafından sağlanan nesneleri this
olarak kullanmanıza izin vermeyen IE8 ve önceki sürümlerde başarısız olacağını unutmayın.
Yaygın sözdizimi (...
)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) kullanın
ES2015'in spread syntax özelliğini bu özelliği destekleyen JavaScript motorlarıyla kullanmak da mümkündür:
var trueArray = [...iterableObject];
Örneğin, bir NodeList
i gerçek bir diziye dönüştürmek istiyorsak, spread sözdizimi ile bu oldukça özlü hale gelir:
var divs = [...document.querySelectorAll("div")];
Array.from` kullanın (spec) | (MDN)
Array.from
(ES2015+, ancak kolayca çoklu doldurulabilir) dizi benzeri bir nesneden bir dizi oluşturur, isteğe bağlı olarak girdileri önce bir eşleme işlevinden geçirir. Yani:
var divs = Array.from(document.querySelectorAll("div"));
Ya da belirli bir sınıfa sahip öğelerin etiket adlarından oluşan bir dizi elde etmek istiyorsanız, mapping fonksiyonunu kullanırsınız:
// Arrow fonksiyonu (ES2015):
var divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
// Standart fonksiyon (Array.from
şimlendirilebildiğinden):
var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {
return element.tagName;
});
Eğer Array.prototype
fonksiyonlarını ana bilgisayar tarafından sağlanan dizi benzeri nesnelerle (DOM listeleri ve JavaScript motoru yerine tarayıcı tarafından sağlanan diğer şeyler) kullanırsanız, ana bilgisayar tarafından sağlanan nesnenin doğru şekilde davrandığından emin olmak için hedef ortamlarınızda test ettiğinizden emin olmanız gerekir. Çoğu düzgün davranır (şimdi), ancak test etmek önemlidir. Bunun nedeni, kullanmak isteyeceğiniz Array.prototype
yöntemlerinin çoğunun, ana bilgisayar tarafından sağlanan nesnenin soyut [[HasProperty]]
işlemine dürüst bir yanıt vermesine dayanmasıdır. Bu yazı itibariyle, tarayıcılar bu konuda çok iyi iş çıkarmaktadır, ancak 5.1 spesifikasyonu ana bilgisayar tarafından sağlanan bir nesnenin dürüst olmama olasılığına izin vermiştir. Bu durum §8.6.2'de, bu bölümün başındaki büyük tablonun birkaç paragraf altında yer almaktadır:
gt; Ana bilgisayar nesneleri, aksi belirtilmedikçe bu dahili yöntemleri herhangi bir şekilde uygulayabilir; örneğin, belirli bir ana bilgisayar nesnesi için [[Get]]
ve [[Put]]
özellik değerlerini gerçekten getirip saklayabilir, ancak [[HasProperty]]
her zaman false üretir.
(ES2015 spesifikasyonunda eşdeğer bir ifade bulamadım, ancak durum hala böyle olabilir). Yine, bu yazı itibariyle modern tarayıcılardaki yaygın ana bilgisayar tarafından sağlanan dizi benzeri nesneler [örneğin NodeList
örnekleri] **doğru bir şekilde [[HasProperty]]
işlemektedir, ancak test etmek önemlidir).
Not: Bu cevap umutsuzca güncelliğini yitirmiştir. Daha modern bir yaklaşım için bir dizi üzerinde kullanılabilen yöntemler'e bakın. İlgi çekici yöntemler şunlar olabilir:
JavaScript]2'de bir diziyi yinelemenin standart yolu vanilla for
döngüsüdür:
var length = arr.length,
element = null;
for (var i = 0; i < length; i++) {
element = arr[i];
// Do something with element
}
Bununla birlikte, bu yaklaşımın yalnızca yoğun bir diziniz varsa ve her dizin bir eleman tarafından işgal ediliyorsa iyi olduğunu unutmayın. Dizi seyrekse, dizide gerçekte var olmayan birçok indis üzerinde yineleme yapacağınızdan, bu yaklaşımla performans sorunlarıyla karşılaşabilirsiniz. Bu durumda, bir for .. in
döngüsü daha iyi bir fikir olabilir. Bununla birlikte, for..in
döngüsü eski tarayıcılarda da numaralandırılacağından veya ek özellikler enumerable
olarak tanımlanmışsa, dizinin yalnızca istenen özelliklerinin (yani dizi öğelerinin) üzerinde işlem yapılmasını sağlamak için uygun önlemleri almalısınız.
ECMAScript 5]3'te dizi prototipi üzerinde bir forEach yöntemi olacaktır, ancak eski tarayıcılarda desteklenmemektedir. Dolayısıyla, bunu tutarlı bir şekilde kullanabilmek için ya bunu destekleyen bir ortama sahip olmanız (örneğin, sunucu tarafı JavaScript için Node.js) ya da bir "Polyfill" kullanmanız gerekir. Bununla birlikte, bu işlevsellik için Polyfill önemsizdir ve kodun okunmasını kolaylaştırdığından, dahil edilmesi iyi bir polyfill'dir.
Bir dizi üzerinde döngü yapmak istiyorsanız, standart üç parçalı for
döngüsünü kullanın.
for (var i = 0; i < myArray.length; i++) {
var arrayItem = myArray[i];
}
myArray.length` dosyasını önbelleğe alarak veya geriye doğru yineleyerek bazı performans optimizasyonları elde edebilirsiniz.