TL; DR
Tetapi ada banyak lagi untuk dijelajahi, baca terus ...
JavaScript memiliki semantik yang kuat untuk perulangan melalui array dan objek seperti array. Saya telah membagi jawaban menjadi dua bagian: Opsi untuk array asli, dan opsi untuk hal-hal yang hanya seperti array , seperti argumentsobjek, objek iterable lainnya (ES2015 +), koleksi DOM, dan sebagainya.
Aku akan segera dicatat bahwa Anda dapat menggunakan ES2015 pilihan sekarang , bahkan pada mesin ES5, oleh transpiling ES2015 untuk ES5. Cari "ES2015 transpiling" / "ES6 transpiling" untuk lebih ...
Oke, mari kita lihat opsi kami:
Untuk Array Aktual
Anda memiliki tiga opsi dalam ECMAScript 5 ("ES5"), versi yang paling banyak didukung saat ini, dan dua lagi ditambahkan dalam ECMAScript 2015 ("ES2015", "ES6"):
- Gunakan
forEachdan terkait (ES5 +)
- Gunakan
forloop sederhana
- Gunakan
for-in dengan benar
- Gunakan
for-of(gunakan iterator secara implisit) (ES2015 +)
- Gunakan iterator secara eksplisit (ES2015 +)
Detail:
1. Gunakan forEachdan terkait
Di lingkungan samar-samar modern (jadi, bukan IE8) di mana Anda memiliki akses ke Arrayfitur yang ditambahkan oleh ES5 (langsung atau menggunakan polyfill), Anda dapat menggunakan forEach( spec| MDN):
var a = ["a", "b", "c"];
a.forEach(function(entry) {
console.log(entry);
});
forEachmenerima fungsi panggilan balik dan, secara opsional, nilai yang digunakan seperti thissaat memanggil panggilan balik itu (tidak digunakan di atas). Callback dipanggil untuk setiap entri dalam array, secara berurutan, melewatkan entri yang tidak ada dalam array jarang. Meskipun saya hanya menggunakan satu argumen di atas, callback dipanggil dengan tiga: Nilai setiap entri, indeks entri itu, dan referensi ke array yang Anda iterasi (jika fungsi Anda belum membuatnya berguna) ).
Kecuali jika Anda mendukung peramban usang seperti IE8 (yang NetApps tunjukkan di lebih dari 4% pangsa pasar pada tulisan ini pada September 2016), Anda dapat dengan senang hati menggunakan forEachlaman web tujuan umum tanpa shim. Jika Anda perlu mendukung browser yang usang, shimming / polyfilling forEachmudah dilakukan (cari "es5 shim" untuk beberapa opsi).
forEach memiliki manfaat bahwa Anda tidak harus mendeklarasikan pengindeksan dan nilai variabel dalam lingkup yang berisi, karena mereka disediakan sebagai argumen untuk fungsi iterasi, dan sangat baik hanya untuk iterasi itu.
Jika Anda khawatir tentang biaya runtime untuk membuat panggilan fungsi untuk setiap entri array, jangan; detail .
Selain itu, forEachadalah fungsi "loop through them all", tetapi ES5 mendefinisikan beberapa fungsi lain yang bermanfaat "bekerja dengan cara Anda melalui array dan melakukan sesuatu" fungsi, termasuk:
every(berhenti mengulang-ulang saat panggilan balik kembali falseatau sesuatu yang palsu)
some(berhenti mengulang-ulang saat panggilan balik kembali trueatau sesuatu yang benar)
filter(membuat array baru termasuk elemen tempat fungsi filter kembali truedan menghilangkan yang mengembalikannya false)
map (membuat array baru dari nilai yang dikembalikan oleh callback)
reduce (membangun nilai dengan berulang kali memanggil callback, meneruskan nilai sebelumnya; lihat spesifikasi untuk detail; berguna untuk menjumlahkan isi array dan banyak hal lainnya)
reduceRight(seperti reduce, tetapi bekerja dalam urutan menurun daripada urutan naik)
2. Gunakan forloop sederhana
Terkadang cara lama adalah yang terbaik:
var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
console.log(a[index]);
}
Jika panjang array tidak akan berubah selama loop, dan itu dalam kode kinerja sensitif (mungkin), versi yang sedikit lebih rumit meraih panjang depan mungkin kecil sedikit lebih cepat:
var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
console.log(a[index]);
}
Dan / atau menghitung mundur:
var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
console.log(a[index]);
}
Tetapi dengan mesin JavaScript modern, jarang Anda perlu menambah sedikit jus terakhir.
Di ES2015 dan lebih tinggi, Anda bisa membuat indeks dan variabel nilai lokal ke forloop:
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"
}
Dan ketika Anda melakukan itu, bukan hanya valuetetapi juga indexdiciptakan kembali untuk setiap iterasi loop, artinya penutupan yang dibuat dalam tubuh loop tetap mengacu pada index(dan value) yang dibuat untuk iterasi spesifik:
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>
Jika Anda memiliki lima div, Anda akan mendapatkan "Indeks adalah: 0" jika Anda mengklik yang pertama dan "Indeks adalah: 4" jika Anda mengklik yang terakhir. Ini tidak berfungsi jika Anda menggunakannya varsebagai gantinya let.
3. Gunakan for-in dengan benar
Anda akan mendapatkan orang memberitahu Anda untuk menggunakan for-in, tapi bukan itu yang for-inbagi . for-inloop melalui properti enumerable suatu objek , bukan indeks array. Pesanan tidak dijamin , bahkan dalam ES2015 (ES6). ES2015 + tidak menentukan untuk properti obyek (melalui [[OwnPropertyKeys]], [[Enumerate]]dan hal-hal yang menggunakannya seperti Object.getOwnPropertyKeys), tapi itu tidak menentukan bahwa for-inakan mengikuti perintah itu; ES2020 melakukannya. (Detail dalam jawaban lain ini .)
Satu-satunya kasus penggunaan nyata untuk for-inpada array adalah:
- Ini array jarang dengan celah besar di dalamnya, atau
- Anda menggunakan properti non-elemen dan Anda ingin memasukkannya dalam loop
Melihat hanya pada contoh pertama itu: Anda dapat menggunakan for-inuntuk mengunjungi elemen array yang jarang itu jika Anda menggunakan pengamanan yang sesuai:
// `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]);
}
}
Perhatikan tiga cek:
Bahwa objek memiliki properti sendiri dengan nama itu (bukan yang diwarisi dari prototipe), dan
Bahwa kuncinya adalah semua digit desimal (misalnya, bentuk string normal, bukan notasi ilmiah), dan
Bahwa nilai kunci ketika dipaksa ke suatu angka adalah <= 2 ^ 32 - 2 (yaitu 4.294.967.294). Dari mana angka itu berasal? Itu bagian dari definisi indeks array dalam spesifikasi . Angka lain (bukan bilangan bulat, angka negatif, angka lebih besar dari 2 ^ 32 - 2) bukan indeks array. Alasannya adalah 2 ^ 32 - 2 adalah yang membuat nilai indeks terbesar lebih rendah dari 2 ^ 32 - 1 , yang merupakan nilai maksimum yang dimiliki sebuah array length. (Misalnya, panjang array cocok dengan bilangan bulat 32-bit unsigned.) (Props untuk RobG untuk menunjukkan dalam komentar di posting blog saya bahwa tes saya sebelumnya tidak benar.)
Anda tidak akan melakukannya dalam kode inline, tentu saja. Anda akan menulis fungsi utilitas. Mungkin:
// 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. Gunakan for-of(gunakan iterator secara implisit) (ES2015 +)
ES2015 menambahkan iterators ke JavaScript. Cara termudah untuk menggunakan iterator adalah for-ofpernyataan baru . Ini terlihat seperti ini:
const a = ["a", "b", "c"];
for (const val of a) {
console.log(val);
}
Di bawah selimut, yang mendapatkan iterator dari array dan loop melalui itu, mendapatkan nilai dari itu. Ini tidak memiliki masalah yang menggunakan for-intelah, karena menggunakan iterator yang ditentukan oleh objek (array), dan array menentukan bahwa iterator mereka mengulangi melalui entri mereka (bukan properti mereka). Tidak seperti for-inES5, urutan entri yang dikunjungi adalah urutan numerik indeks mereka.
5. Gunakan iterator secara eksplisit (ES2015 +)
Terkadang, Anda mungkin ingin menggunakan iterator secara eksplisit . Anda dapat melakukannya juga, meskipun jauh lebih clunkier daripada for-of. Ini terlihat seperti ini:
const a = ["a", "b", "c"];
const it = a.values();
let entry;
while (!(entry = it.next()).done) {
console.log(entry.value);
}
Iterator adalah objek yang cocok dengan definisi Iterator dalam spesifikasi. Its nextmetode mengembalikan baru hasil objek setiap kali Anda menyebutnya. Objek hasil memiliki properti, donememberi tahu kami apakah itu dilakukan, dan properti valuedengan nilai untuk iterasi itu. ( doneadalah opsional jika itu akan false, valueadalah opsional jika itu akan terjadi undefined.)
Arti valuebervariasi tergantung pada iterator; dukungan array (setidaknya) tiga fungsi yang mengembalikan iterator:
values(): Ini yang saya gunakan di atas. Ini mengembalikan sebuah iterator di mana setiap valueadalah entri array untuk iterasi yang ( "a", "b", dan "c"dalam contoh sebelumnya).
keys(): Mengembalikan iterator di mana masing value- masing adalah kunci untuk iterasi itu (jadi untuk di aatas kita , itu akan menjadi "0", lalu "1", lalu "2").
entries(): Mengembalikan iterator di mana masing value- masing adalah array dalam bentuk [key, value]untuk iterasi itu.
Untuk Objek Seperti Array
Selain dari array yang benar, ada juga objek seperti array yang memiliki lengthproperti dan properti dengan nama numerik: NodeListinstance, argumentsobjek, dll. Bagaimana cara kita menelusuri isinya?
Gunakan salah satu opsi di atas untuk array
Setidaknya beberapa, dan mungkin sebagian besar atau bahkan semua, pendekatan array di atas sering berlaku sama baiknya untuk objek seperti array:
Gunakan forEachdan terkait (ES5 +)
Berbagai fungsi pada Array.prototype"sengaja generik" dan biasanya dapat digunakan pada objek mirip array melalui Function#callatau Function#apply. (Lihat Peringatan untuk objek yang disediakan oleh tuan rumah di akhir jawaban ini, tetapi ini adalah masalah yang jarang terjadi.)
Misalkan Anda ingin menggunakan forEachpada Node's childNodesproperti. Anda akan melakukan ini:
Array.prototype.forEach.call(node.childNodes, function(child) {
// Do something with `child`
});
Jika Anda akan sering melakukannya, Anda mungkin ingin mengambil salinan referensi fungsi ke dalam variabel untuk digunakan kembali, misalnya:
// (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`
});
Gunakan forloop sederhana
Jelas, forloop sederhana berlaku untuk objek mirip array.
Gunakan for-in dengan benar
for-indengan perlindungan yang sama dengan array harus bekerja dengan objek seperti array juga; peringatan untuk objek yang disediakan oleh host pada # 1 di atas mungkin berlaku.
Gunakan for-of(gunakan iterator secara implisit) (ES2015 +)
for-ofmenggunakan iterator yang disediakan oleh objek (jika ada). Itu termasuk objek yang disediakan host. Misalnya, spesifikasi untuk NodeListdari querySelectorAlltelah diperbarui untuk mendukung iterasi. Spesifikasi untuk HTMLCollectiondari getElementsByTagNametidak.
Gunakan iterator secara eksplisit (ES2015 +)
Lihat # 4.
Buat array yang benar
Di lain waktu, Anda mungkin ingin mengubah objek mirip array menjadi array yang benar. Melakukan hal itu sangat mudah:
Gunakan slicemetode array
Kita dapat menggunakan slicemetode array, yang seperti metode lain yang disebutkan di atas adalah "sengaja generik" dan dapat digunakan dengan objek seperti array, seperti ini:
var trueArray = Array.prototype.slice.call(arrayLikeObject);
Jadi misalnya, jika kita ingin mengkonversi NodeListmenjadi array yang benar, kita bisa melakukan ini:
var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
Lihat Peringatan untuk objek yang disediakan oleh host di bawah ini. Secara khusus, perhatikan bahwa ini akan gagal di IE8 dan sebelumnya, yang tidak memungkinkan Anda menggunakan objek yang disediakan host thisseperti itu.
Gunakan sintaksis sebar ( ...)
Dimungkinkan juga untuk menggunakan sintaksis penyebaran ES2015 dengan mesin JavaScript yang mendukung fitur ini. Seperti for-of, ini menggunakan iterator yang disediakan oleh objek (lihat # 4 di bagian sebelumnya):
var trueArray = [...iterableObject];
Jadi misalnya, jika kita ingin mengkonversi NodeListmenjadi array yang benar, dengan menyebar sintaks ini menjadi sangat ringkas:
var divs = [...document.querySelectorAll("div")];
Menggunakan Array.from
Array.from (spec) | (MDN) (ES2015 +, tetapi mudah di-polyfilled) membuat array dari objek mirip array, secara opsional meneruskan entri melalui fungsi pemetaan terlebih dahulu. Begitu:
var divs = Array.from(document.querySelectorAll("div"));
Atau jika Anda ingin mendapatkan array nama tag elemen dengan kelas yang diberikan, Anda akan menggunakan fungsi pemetaan:
// 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;
});
Peringatan untuk objek yang disediakan oleh host
Jika Anda menggunakan Array.prototypefungsi dengan objek mirip array yang disediakan host (daftar DOM dan hal-hal lain yang disediakan oleh browser daripada mesin JavaScript), Anda perlu memastikan untuk menguji di lingkungan target Anda untuk memastikan objek yang disediakan host bertindak dengan benar . Sebagian besar berperilaku baik (sekarang), tetapi penting untuk menguji. Alasannya adalah bahwa sebagian besar Array.prototypemetode yang Anda ingin gunakan bergantung pada objek yang disediakan host memberikan jawaban jujur untuk [[HasProperty]]operasi abstrak . Pada tulisan ini, browser melakukan pekerjaan yang sangat baik ini, tetapi spesifikasi 5.1 memang memungkinkan untuk kemungkinan objek yang disediakan host mungkin tidak jujur. Ada dalam §8.6.2 , beberapa paragraf di bawah tabel besar di dekat awal bagian itu), di mana dikatakan:
Objek host dapat menerapkan metode internal ini dengan cara apa pun kecuali ditentukan lain; misalnya, satu kemungkinan adalah bahwa [[Get]]dan [[Put]]untuk objek host tertentu memang mengambil dan menyimpan nilai properti tetapi [[HasProperty]]selalu menghasilkan false .
(Saya tidak bisa menemukan setara verbiage di ES2015 spec, tapi itu pasti masih menjadi kasus.) Sekali lagi, seperti tulisan ini umum host-disediakan array seperti objek dalam browser modern [ NodeListcontoh, misalnya] lakukan pegangan [[HasProperty]]dengan benar, tetapi penting untuk menguji.)
forEachdan bukan hanyafor. seperti yang dinyatakan, dalam c # itu sedikit berbeda, dan itu membingungkan saya :)