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 arguments
objek, 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
forEach
dan terkait (ES5 +)
- Gunakan
for
loop sederhana
- Gunakan
for-in
dengan benar
- Gunakan
for-of
(gunakan iterator secara implisit) (ES2015 +)
- Gunakan iterator secara eksplisit (ES2015 +)
Detail:
1. Gunakan forEach
dan terkait
Di lingkungan samar-samar modern (jadi, bukan IE8) di mana Anda memiliki akses ke Array
fitur 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);
});
forEach
menerima fungsi panggilan balik dan, secara opsional, nilai yang digunakan seperti this
saat 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 forEach
laman web tujuan umum tanpa shim. Jika Anda perlu mendukung browser yang usang, shimming / polyfilling forEach
mudah 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, forEach
adalah 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 false
atau sesuatu yang palsu)
some
(berhenti mengulang-ulang saat panggilan balik kembali true
atau sesuatu yang benar)
filter
(membuat array baru termasuk elemen tempat fungsi filter kembali true
dan 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 for
loop 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 for
loop:
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 value
tetapi juga index
diciptakan 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 var
sebagai gantinya let
.
3. Gunakan for-in
dengan benar
Anda akan mendapatkan orang memberitahu Anda untuk menggunakan for-in
, tapi bukan itu yang for-in
bagi . for-in
loop 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-in
akan mengikuti perintah itu; ES2020 melakukannya. (Detail dalam jawaban lain ini .)
Satu-satunya kasus penggunaan nyata untuk for-in
pada 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-in
untuk 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-of
pernyataan 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-in
telah, karena menggunakan iterator yang ditentukan oleh objek (array), dan array menentukan bahwa iterator mereka mengulangi melalui entri mereka (bukan properti mereka). Tidak seperti for-in
ES5, 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 next
metode mengembalikan baru hasil objek setiap kali Anda menyebutnya. Objek hasil memiliki properti, done
memberi tahu kami apakah itu dilakukan, dan properti value
dengan nilai untuk iterasi itu. ( done
adalah opsional jika itu akan false
, value
adalah opsional jika itu akan terjadi undefined
.)
Arti value
bervariasi 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 value
adalah 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 a
atas 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 length
properti dan properti dengan nama numerik: NodeList
instance, arguments
objek, 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 forEach
dan terkait (ES5 +)
Berbagai fungsi pada Array.prototype
"sengaja generik" dan biasanya dapat digunakan pada objek mirip array melalui Function#call
atau 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 forEach
pada Node
's childNodes
properti. 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 for
loop sederhana
Jelas, for
loop sederhana berlaku untuk objek mirip array.
Gunakan for-in
dengan benar
for-in
dengan 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-of
menggunakan iterator yang disediakan oleh objek (jika ada). Itu termasuk objek yang disediakan host. Misalnya, spesifikasi untuk NodeList
dari querySelectorAll
telah diperbarui untuk mendukung iterasi. Spesifikasi untuk HTMLCollection
dari getElementsByTagName
tidak.
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 slice
metode array
Kita dapat menggunakan slice
metode 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 NodeList
menjadi 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 this
seperti 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 NodeList
menjadi 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.prototype
fungsi 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.prototype
metode 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 [ NodeList
contoh, misalnya] lakukan pegangan [[HasProperty]]
dengan benar, tetapi penting untuk menguji.)
forEach
dan bukan hanyafor
. seperti yang dinyatakan, dalam c # itu sedikit berbeda, dan itu membingungkan saya :)