JavaScript untuk ... dalam vs untuk

461

Apakah Anda pikir ada perbedaan besar dalam untuk ... dalam dan untuk loop? Jenis "untuk" apa yang Anda pilih untuk digunakan dan mengapa?

Katakanlah kita memiliki array array asosiatif:

var myArray = [{'key': 'value'}, {'key': 'value1'}];

Jadi kita bisa beralih:

for (var i = 0; i < myArray.length; i++)

Dan:

for (var i in myArray)

Saya tidak melihat perbedaan besar. Apakah ada masalah kinerja?

andrii
sumber
13
Perhatikan bahwa kami juga, seperti dari JS 1,6 , memiliki: myArray.forEach(callback[, thisarg]).
Benji XVI
14
@Benji array.forEach sebenarnya di ES5.
mikemaccana
2
dalam lingkaran for-in Anda memerlukan persyaratan yang terlihat seperti ini:if(myArray.hasOwnProperty(i)){true}
Eric Hodonsky
6
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); OK untuk menggunakan cukup banyak di mana-mana kecuali di IE8- dan sejauh ini sintaks yang paling elegan
Jon z
5
Ada juga for...ofpernyataan dalam ECMAScript 6 , misalnya:for (let i of myArray) console.log(i);
Vitalii Fedorenko

Jawaban:

548

Pilihannya harus didasarkan pada idiom mana yang paling dipahami.

Array diulang menggunakan:

for (var i = 0; i < a.length; i++)
   //do stuff with a[i]

Objek yang digunakan sebagai array asosiatif diulang menggunakan:

for (var key in o)
  //do stuff with o[key]

Kecuali Anda memiliki alasan untuk menghancurkan bumi, patuhi pola penggunaan yang telah ditetapkan.

AnthonyWJones
sumber
38
Harus disebutkan bahwa ini adalah praktik yang baik untuk digunakan ... dengan memfilter pernyataan if. Ada metode berguna Object "obj.hasOwnProperty (anggota)" yang memeriksa apakah anggota yang dikembalikan oleh iterator sebenarnya adalah anggota objek. Lihat: javascript.crockford.com/code.html
Damir Zekić
57
Seperti dikomentari pada jawaban lain, "untuk ... dalam" tidak berfungsi dengan benar untuk Array, karena akan mengulangi semua properti dan metode Array. Dengan demikian, Anda harus menggunakan "for ... in" hanya untuk beralih pada properti objek. Kalau tidak, tetap berpegang pada "untuk (i = 0; i <sesuatu; i ++)"
Denilson Sá Maia
Untuk alasan kinerja, IMO lebih baik mengevaluasi panjang array sebelum for, bukan mengevaluasi a.length setiap kali dalam loop.
UpTheCreek
9
@UpTheCreek: Itu benar ketika array sebenarnya adalah sesuatu yang dikembalikan oleh HTMLDOM, namun, saya bertanya-tanya seberapa besar array standar javascript perlu sebelum Anda bisa melihat perbedaan yang cukup besar? Secara pribadi saya akan menjaga kode sesederhana mungkin sampai terbukti perlu untuk melakukan sesuatu yang berbeda.
AnthonyWJones
2
@Pichan saya pikir maksud Anda i < l, tidak i < a, dalam kondisi for-loop Anda.
Max Nanasy
161

Douglas Crockford merekomendasikan dalam JavaScript: The Good Parts (halaman 24) untuk menghindari penggunaan for inpernyataan.

Jika Anda menggunakan for inuntuk mengulang nama properti di suatu objek, hasilnya tidak diurutkan. Lebih buruk: Anda mungkin mendapatkan hasil yang tidak terduga; itu termasuk anggota yang diwarisi dari rantai prototipe dan nama metode.

Semuanya kecuali properti dapat disaring .hasOwnProperty. Contoh kode ini melakukan apa yang awalnya Anda inginkan:

for (var name in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, name)) {
        // DO STUFF
    }
}
Benno Richters
sumber
70
untuk ... di sangat tepat untuk mengulang properti objek. Tidak tepat untuk mengulang elemen array. Jika Anda tidak dapat memahami perbedaan antara skenario ini, maka ya, Anda harus menghindari untuk ... di; jika tidak, gila.
Shog9
8
Ingin menekankan fakta bahwa BUKANLAH PERINTAH! Ini bisa menjadi masalah besar, dan akan sulit ditangkap.
Jason
4
+1 untuk "itu termasuk anggota yang diwarisi dari rantai prototipe dan nama metode." Anda akan bersenang-senang jika seseorang menggunakan kode Anda dengan Prototipe yang dimuat (bahkan jika kode Anda tidak benar-benar menggunakannya), misalnya.
ijw
13
Jangan lupa untuk mendeklarasikan pada namevariabel: for(var name in object)..., sebaliknya, jika kode yang ada di dalam fungsi misalnya, namevariabel akhir sampai menjadi properti dari obyek global (tugas ke identifier dideklarasikan melakukan itu), juga di ECMAScript baru 5 Mode Ketat, kode itu akan melempar a ReferenceError.
CMS
6
@Nosredna: Ada masalah tentang urutan iterasi untuk Chrome, yang diajukan oleh John Resig, yang ditandai sebagai WontFix. code.google.com/p/chromium/issues/detail?id=883 . Bahkan sebelum chrome, urutan iterasi tidak sama di seluruh browser jika Anda menghapus dan kemudian menambahkan properti lagi. IE 9 juga berperilaku seperti chrome (seharusnya untuk peningkatan kecepatan). Jadi ... Tolong berhenti menyebarkan informasi yang tidak akurat, Anda akan sangat naif untuk tetap bergantung padanya.
Juan Mendes
62

FYI - Pengguna jQuery


each(callback)Metode jQuery menggunakan for( ; ; )loop secara default, dan for( in ) hanya akan digunakan jika panjangnya undefined.

Oleh karena itu, saya akan mengatakan aman untuk mengasumsikan urutan yang benar ketika menggunakan fungsi ini.

Contoh :

$(['a','b','c']).each(function() {
    alert(this);
});
//Outputs "a" then "b" then "c"

Kelemahan dari menggunakan ini adalah bahwa jika Anda melakukan beberapa logika non UI, fungsi Anda akan lebih portabel untuk kerangka kerja lainnya. The each()fungsi mungkin terbaik dicadangkan untuk digunakan dengan pemilih jQuery dan for( ; ; )mungkin dianjurkan sebaliknya.


Jason
sumber
4
Selalu ada documentcloud.github.com/underscore yang memiliki _.each dan banyak fungsi bermanfaat lainnya
w00t
1
apakah itu juga berarti jika saya memiliki properti panjang di objek saya $ .each akan gagal? misal x = {a: "1", b: "2", panjang: 3}.
Onur Topal
29

ada perbedaan kinerja tergantung pada jenis loop apa yang Anda gunakan dan pada browser apa.

Contohnya:

for (var i = myArray.length-1; i >= 0; i--)

hampir dua kali lebih cepat pada beberapa browser dibandingkan:

for (var i = 0; i < myArray.length; i++)

Namun, kecuali jika array Anda BESAR atau Anda memutarnya secara konstan, semuanya cukup cepat. Saya sangat ragu bahwa array looping adalah hambatan dalam proyek Anda (atau untuk proyek lain dalam hal ini)

Gene
sumber
5
Apakah menyimpan "myArray.length" ke dalam variabel sebelum perulangan membuat perbedaan kinerja hilang? Dugaan saya adalah "ya".
Tomalak
3
'MyArray.length' adalah properti, bukan metode pada objek - tidak ada perhitungan yang dilakukan untuk menentukan nilainya. Menyimpan nilainya dalam suatu variabel tidak akan menghasilkan apa-apa.
jason
3
Ya ada. Properti bukan variabel; mereka memang mendapatkan / mengatur kode.
ste
12
Saya cenderung menggunakanfor(var i = myArray.length; i--;)
Kevin
2
Kode untuk kejelasan dan keterbacaan. Bukan untuk apa yang terjadi untuk menjalankan sedikit lebih cepat di beberapa browser ketika menggunakan array besar bukan kepalang pada saat ini dalam waktu. Optimalisasi dapat berubah, tetapi pembacaan kode Anda (atau ketiadaannya) tidak akan. Tulis kode yang mudah diikuti orang lain, dan biarkan pengoptimal mengejar waktu yang ditentukan.
Agustus
26

Perhatikan bahwa metode Array.forEach asli sekarang banyak didukung .

Sam Dutton
sumber
2
Apa fungsinya? Apakah ada masalah yang disebutkan dalam postingan lain (perulangan properti alih-alih elemen array)? Juga, karena IE8 tidak mendukungnya, agak sulit untuk mengatakan bahwa ia didukung secara luas.
Rauni Lillemets
2
sebanyak * pengguna nix membenci itu, IE8 adalah sebagian besar dari semua pengguna sub-windows7. Itulah sebagian besar pasar browser.
sbartell
2
@Rauni - Saya setuju, tetapi untuk perangkat desktop, pangsa browser IE kurang dari 40%, menurut en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table , dan menurut marketshare.hitslink.com/… dan situs lainnya, setidaknya 8% browser adalah IE 9. Dengan kata lain, Array.forEach didukung oleh sekitar 70% browser desktop, jadi saya rasa 'dukungan luas' tidak masuk akal. Saya belum memeriksa, tetapi dukungan seluler (di browser WebKit dan Opera) mungkin bahkan lebih tinggi. Jelas, ada variasi yang cukup besar secara geografis.
Sam Dutton
1
Terima kasih atas pembaruannya. Saya setuju bahwa ini dapat dikatakan "didukung secara luas". Satu-satunya masalah adalah bahwa jika pengguna menggunakan metode JS ini, ia masih harus menulis metode cadangan untuk
kasing
1
@Rauni - Anda dapat menggunakan es5-shim dan es6-shim untuk secara otomatis menyediakan metode cadangan. github.com/es-shims/es5-shim
Michiel van der Blonk
24

Jawaban yang diperbarui untuk 2012 versi terbaru dari semua browser utama - Chrome, Firefox, IE9, Safari dan Opera mendukung larik asli ES5. ForEach

Kecuali Anda memiliki alasan untuk mendukung IE8 secara native (dengan mengingat ES5-shim atau bingkai Chrome dapat diberikan kepada pengguna ini, yang akan memberikan lingkungan JS yang tepat), lebih bersih jika hanya menggunakan sintaksis bahasa yang tepat:

myArray.forEach(function(item, index) {
    console.log(item, index);
});

Dokumentasi lengkap untuk array.forEach () ada di MDN.

mikemaccana
sumber
1
Anda harus benar-benar mendokumentasikan parameter callback: 1 nilai elemen, 2 indeks elemen, 3 array dilintasi
drewish
Saya mendengar apa yang Anda katakan, tetapi dalam hal ini penyederhanaan yang berlebihan mengaburkan berbagai kemungkinan. Memiliki kedua indeks dan nilai berarti dapat berfungsi sebagai pengganti untuk ... di dan untuk masing-masing ... masuk — dengan bonus yang tidak harus Anda ingat yang beralih pada kunci atau nilai.
drewish
1
@Cory: ES5 forEach dapat ditambahkan ke browser ES3 lama dengan cukup mudah. Lebih sedikit kode adalah kode yang lebih baik.
mikemaccana
2
@ thumbnailer Apakah ini dapat digunakan pada array dan objek secara bergantian?
hitautodestruct
1
@hitautodestruct Ini bagian dari prototipe Array, bukan Object. Secara umum di komunitas saat ini objek non-Array masih diulang dengan 'for (var key in object) {}'.
mikemaccana
14

Keduanya tidak sama ketika array jarang.

var array = [0, 1, 2, , , 5];

for (var k in array) {
  // Not guaranteed by the language spec to iterate in order.
  alert(k);  // Outputs 0, 1, 2, 5.
  // Behavior when loop body adds to the array is unclear.
}

for (var i = 0; i < array.length; ++i) {
  // Iterates in order.
  // i is a number, not a string.
  alert(i);  // Outputs 0, 1, 2, 3, 4, 5
  // Behavior when loop body modifies array is clearer.
}
Mike Samuel
sumber
14

Menggunakan forEach untuk melewati rantai prototipe

Hanya tambahan cepat untuk jawaban @ nailer di atas , menggunakan forEach dengan Object.keys berarti Anda dapat menghindari iterasi melalui rantai prototipe tanpa harus menggunakan hasOwnProperty.

var Base = function () {
    this.coming = "hey";
};

var Sub = function () {
    this.leaving = "bye";
};

Sub.prototype = new Base();
var tst = new Sub();

for (var i in tst) {
    console.log(tst.hasOwnProperty(i) + i + tst[i]);
}

Object.keys(tst).forEach(function (val) {
    console.log(val + tst[val]);
});
meloncholy
sumber
2
sial, itu licik. Perlu membaca 50 posting lainnya untuk mendapatkan ini. obj = {"pink": "bebek", merah: "angsa"}; Object.keys (obj) === ["pink", "red"]
Orwellophile
14

Saya berpendapat kedua bahwa Anda harus memilih metode iterasi sesuai dengan kebutuhan Anda. Saya akan menyarankan Anda sebenarnya untuk tidak pernah melalui asli Arraydengan for instruktur. Ini jauh lebih lambat dan , seperti yang ditunjukkan Chase Seibert saat ini, tidak kompatibel dengan kerangka kerja Prototype.

Ada tolok ukur yang sangat baik pada berbagai gaya perulangan yang harus Anda perhatikan jika Anda bekerja dengan JavaScript . Jangan melakukan optimasi awal, tetapi Anda harus menyimpannya di belakang kepala Anda.

Saya akan menggunakan for inuntuk mendapatkan semua properti dari suatu objek, yang sangat berguna saat men-debug skrip Anda. Misalnya, saya suka membuat garis ini berguna ketika saya menjelajahi objek yang tidak dikenal:

l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.log(l);

Itu membuang konten dari seluruh objek (bersama-sama dengan badan metode) ke log Firebug saya. Sangat berguna.

Damir Zekić
sumber
Tautan sekarang terputus. Tentu ingin melihat patokan, jika seseorang memiliki tautan lain.
Billbad
Tidak rusak lagi.
Olli
Loop Foreach rusak dalam prototipe? Seperti yang sekarang umum didukung, itu adalah sesuatu yang harus diselesaikan oleh prototipe.
mvrak
7

ini adalah sesuatu yang saya lakukan.

function foreach(o, f) {
 for(var i = 0; i < o.length; i++) { // simple for loop
  f(o[i], i); // execute a function and make the obj, objIndex available
 }
}

ini adalah bagaimana Anda akan menggunakannya
ini akan bekerja pada array dan objek (seperti daftar elemen HTML)

foreach(o, function(obj, i) { // for each obj in o
  alert(obj); // obj
  alert(i); // obj index
  /*
    say if you were dealing with an html element may be you have a collection of divs
  */
  if(typeof obj == 'object') { 
   obj.style.marginLeft = '20px';
  }
});

Saya baru saja membuat ini jadi saya terbuka untuk saran :)


sumber
Hal-hal hebat - sangat mudah!
Chris
6

Saya akan menggunakan metode yang berbeda berdasarkan pada bagaimana saya ingin referensi item.

Gunakan foreach jika Anda hanya ingin item saat ini.

Gunakan untuk jika Anda memerlukan pengindeks untuk melakukan perbandingan relatif. (Yaitu, bagaimana ini dibandingkan dengan item sebelumnya / berikutnya?)

Saya tidak pernah memperhatikan perbedaan kinerja. Saya akan menunggu sampai memiliki masalah kinerja sebelum mengkhawatirkannya.

Matt Lacey
sumber
Lihat jawaban Bnos di bawah - untuk ... di tidak melakukan apa yang Anda harapkan di sini dan jika Anda menggunakannya Anda mungkin akan bersenang-senang. Sebagai catatan, Prototipe melakukan hal-hal dengan cara yang benar .
marcus.greasly
4

Dengan for (var i di myArray) Anda juga dapat mengulang objek, saya akan berisi nama kunci dan Anda dapat mengakses properti melalui myArray [i] . Selain itu, semua metode yang akan Anda tambahkan ke objek akan dimasukkan dalam loop, juga, yaitu, jika Anda menggunakan kerangka kerja eksternal seperti jQuery atau prototipe, atau jika Anda menambahkan metode ke objek prototipe secara langsung, pada satu titik saya akan menunjuk ke metode-metode itu.

pilsetnieks
sumber
4

Awas!

Misalnya, jika Anda memiliki beberapa tag skrip dan sedang mencari informasi dalam atribut tag, Anda harus menggunakan properti .length dengan for for karena itu bukan array sederhana melainkan objek HTMLCollection.

https://developer.mozilla.org/en/DOM/HTMLCollection

Jika Anda menggunakan pernyataan foreach untuk (var i di yourList) itu akan mengembalikan properti dan metode HTMLCollection di sebagian besar browser!

var scriptTags = document.getElementsByTagName("script");

for(var i = 0; i < scriptTags.length; i++)
alert(i); // Will print all your elements index (you can get src attribute value using scriptTags[i].attributes[0].value)

for(var i in scriptTags)
alert(i); // Will print "length", "item" and "namedItem" in addition to your elements!

Bahkan jika getElementsByTagName harus mengembalikan NodeList, sebagian besar browser mengembalikan HTMLCollection: https://developer.mozilla.org/en/DOM/document.getElementsByTagName

baptx
sumber
3

Karena dalam loop pada Array tidak kompatibel dengan Prototipe. Jika Anda berpikir Anda mungkin perlu menggunakan perpustakaan itu di masa depan, masuk akal untuk berpegang teguh pada loop.

http://www.prototypejs.org/api/array

Chase Seibert
sumber
Lupakan "Anda mungkin perlu menggunakan perpustakaan itu". Pikirkan sebaliknya "JS Anda mungkin disertakan dengan hal lain yang menggunakan perpustakaan itu", karena masalahnya masih ada.
ijw
3

Saya telah melihat masalah dengan "untuk masing-masing" menggunakan objek dan prototipe dan array

pemahaman saya adalah bahwa untuk masing-masing adalah untuk properti objek dan BUKAN array

Benjamin Lee
sumber
3

Jika Anda benar-benar ingin mempercepat kode Anda, bagaimana dengan itu?

for( var i=0,j=null; j=array[i++]; foo(j) );

itu agak memiliki logika sementara dalam pernyataan untuk dan itu kurang berlebihan. Juga firefox memiliki Array.forEach dan Array.filter

fabjoa
sumber
2
Mengapa ini mempercepat kode Anda? Saya tidak bisa melihat mengapa menyusun ulang pernyataan seperti ini akan mempercepatnya.
Rup
3

Kode yang lebih pendek dan terbaik menurut jsperf adalah

keys  = Object.keys(obj);
for (var i = keys.length; i--;){
   value = obj[keys[i]];// or other action
}
bormat
sumber
1

Gunakan array Array (). ForEach untuk memanfaatkan paralelisme

PazoozaTest Pazman
sumber
4
JavaScript di browser adalah event loop bersamaan sehingga Array.prototype.forEachtidak akan mengeksekusi beberapa panggilan ke callback secara paralel.
Mike Samuel
1

untuk (;;) adalah untuk Array : [20,55,33]

for..in adalah untuk Objects : {x: 20, y: 55: z: 33}

angelito
sumber
0

Hati-hati!!! Saya menggunakan Chrome 22.0 di Mac OS dan saya mengalami masalah dengan untuk setiap sintaks.

Saya tidak tahu apakah ini masalah browser, masalah javascript atau beberapa kesalahan dalam kode, tapi ini SANGAT aneh. Di luar objek itu bekerja dengan sempurna.

var MyTest = {
    a:string = "a",
    b:string = "b"
};

myfunction = function(dicts) {
    for (var dict in dicts) {
        alert(dict);
        alert(typeof dict); // print 'string' (incorrect)
    }

    for (var i = 0; i < dicts.length; i++) {
        alert(dicts[i]);
        alert(typeof dicts[i]); // print 'object' (correct, it must be {abc: "xyz"})
    }
};

MyObj = function() {
    this.aaa = function() {
        myfunction([MyTest]);
    };
};
new MyObj().aaa(); // This does not work

myfunction([MyTest]); // This works
Paulo Check
sumber
0

Ada perbedaan penting antara keduanya. For-in iterate atas properti dari suatu objek, jadi ketika case adalah array, ia tidak hanya akan beralih pada elemen-elemennya tetapi juga pada fungsi "remove" yang dimilikinya.

for (var i = 0; i < myArray.length; i++) { 
    console.log(i) 
}

//Output
0
1

for (var i in myArray) { 
    console.log(i) 
} 

// Output
0 
1 
remove

Anda bisa menggunakan for-in dengan if(myArray.hasOwnProperty(i)). Namun, ketika mengulangi array saya selalu lebih suka untuk menghindari ini dan hanya menggunakan pernyataan for (;;).

achecopar
sumber
0

Meskipun keduanya sangat mirip, ada perbedaan kecil:

var array = ["a", "b", "c"];
array["abc"] = 123;
console.log("Standard for loop:");
for (var index = 0; index < array.length; index++)
{
  console.log(" array[" + index + "] = " + array[index]); //Standard for loop
}

dalam hal ini outputnya adalah:

STANDAR UNTUK LOOP:

ARRAY [0] = A

ARRAY [1] = B

ARRAY [2] = C

console.log("For-in loop:");
for (var key in array)
{
  console.log(" array[" + key + "] = " + array[key]); //For-in loop output
}

sedangkan dalam hal ini outputnya adalah:

FOR-IN LOOP:

ARRAY [1] = B

ARRAY [2] = C

ARRAY [10] = D

ARRAY [ABC] = 123

Yash Srivastava
sumber
0

Pernyataan dalam untuk memungkinkan untuk perulangan melalui nama-nama semua properti dari suatu objek. Sayangnya, itu juga loop melalui semua anggota yang diwarisi melalui rantai prototipe. Ini memiliki efek samping yang buruk dari melayani fungsi metode ketika bunga ada di anggota data.

Fayaz
sumber