Mengapa menggunakan "untuk ... dalam" dengan iterasi array adalah ide yang buruk?

1824

Saya diberitahu untuk tidak menggunakan for...inarray dalam JavaScript. Kenapa tidak?

lYriCAsSH
sumber
45
Saya melihat pertanyaan baru-baru ini di mana seseorang mengatakan itu kepada Anda, tetapi mereka hanya dimaksudkan untuk Array. Ini dianggap praktik buruk untuk iterasi melalui array tetapi tidak harus untuk iterasi melalui anggota suatu objek.
mmurch
19
Banyak jawaban dengan loop "for" seperti 'for (var i = 0; i <hColl.length; i ++) {}' dibandingkan dengan 'var i = hColl.length; sedangkan (i--) {} 'yang, ketika dimungkinkan untuk menggunakan bentuk yang terakhir itu jauh lebih cepat. Saya tahu ini tangensial tetapi saya pikir saya akan menambahkan sedikit ini.
Mark Schultheiss
2
@MarkSchultheiss tapi itu iterasi terbalik. Apakah ada versi iterasi maju yang lebih cepat?
ma11hew28
5
@Wynand gunakan var i = hCol1.length; for (i;i;i--;) {}cache i karena akan membuat perbedaan, dan menyederhanakan tes. - semakin tua peramban, semakin banyak perbedaan antara fordan whileSELALU cache penghitung "i" - dan tentu saja negatif tidak selalu cocok dengan situasinya, dan semakin negatif sementara obfuscate kode sedikit bagi sebagian orang. dan perhatikan var i = 1000; for (i; i; i--) {}dan var b =1000 for (b; b--;) {}kemana saya pergi 1000 ke 1 dan b pergi 999 ke 0. - semakin lama browser, semakin cenderung mendukung kinerja.
Mark Schultheiss
9
Anda juga bisa pintar. for(var i = 0, l = myArray.length; i < l; ++i) ...adalah yang tercepat dan terbaik yang bisa Anda dapatkan dengan iterasi maju.
Mathieu Amiot

Jawaban:

1557

Alasannya adalah bahwa satu konstruksi:

var a = []; // Create a new empty array.
a[5] = 5;   // Perfectly legal JavaScript that resizes the array.

for (var i = 0; i < a.length; i++) {
    // Iterate over numeric indexes from 0 to 5, as everyone expects.
    console.log(a[i]);
}

/* Will display:
   undefined
   undefined
   undefined
   undefined
   undefined
   5
*/

kadang-kadang bisa sangat berbeda dari yang lain:

var a = [];
a[5] = 5;
for (var x in a) {
    // Shows only the explicitly set index of "5", and ignores 0-4
    console.log(x);
}

/* Will display:
   5
*/

Juga pertimbangkan bahwa perpustakaan JavaScript mungkin melakukan hal-hal seperti ini, yang akan memengaruhi array apa pun yang Anda buat:

// Somewhere deep in your JavaScript library...
Array.prototype.foo = 1;

// Now you have no idea what the below code will do.
var a = [1, 2, 3, 4, 5];
for (var x in a){
    // Now foo is a part of EVERY array and 
    // will show up here as a value of 'x'.
    console.log(x);
}

/* Will display:
   0
   1
   2
   3
   4
   foo
*/

Triptych
sumber
146
Secara historis, beberapa browser bahkan mengulang lebih dari 'panjang', 'toString' dll!
bobince
398
Ingatlah untuk menggunakan (var x in a)daripada (x in a)- tidak ingin membuat global.
Chris Morgan
78
Masalah pertama bukan alasan itu buruk, hanya perbedaan dalam semantik. Masalah kedua menurut saya merupakan alasan (di atas bentrokan antara perpustakaan melakukan hal yang sama) bahwa mengubah prototipe dari datatype bawaan adalah buruk, daripada itu untuk..dalam buruk.
Stewart
86
@Stewart: Semua objek di JS asosiatif. Array JS adalah objek, jadi ya, itu asosiatif juga, tapi bukan itu gunanya. Jika Anda ingin mengulangi kunci objek , gunakan for (var key in object). Jika Anda ingin mengulangi elemen array , gunakan for(var i = 0; i < array.length; i += 1).
Martijn
42
Anda mengatakan untuk contoh pertama, bahwa itu Iterate atas indeks numerik dari 0 hingga 4, karena semua orang mengharapkan , saya berharap untuk beralih dari 0 hingga 5 ! Karena jika Anda menambahkan elemen di posisi 5, array akan memiliki 6 elemen (5 di antaranya tidak terdefinisi).
stivlo
393

The for-inPernyataan itu sendiri tidak "praktek buruk", namun dapat disalahgunakan , misalnya, untuk iterate lebih dari array atau array-seperti objek.

Tujuan dari for-inpernyataan ini adalah untuk menghitung lebih dari properti objek. Pernyataan ini akan muncul dalam rantai prototipe, juga menyebutkan sifat-sifat yang diwariskan , suatu hal yang terkadang tidak diinginkan.

Juga, urutan iterasi tidak dijamin oleh spec., Yang berarti bahwa jika Anda ingin "mengulangi" objek array, dengan pernyataan ini Anda tidak dapat yakin bahwa properti (indeks array) akan dikunjungi dalam urutan numerik.

Misalnya, dalam JScript (IE <= 8), urutan enumerasi bahkan pada objek Array didefinisikan ketika properti dibuat:

var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';

for (var p in array) {
  //... p will be "2", "1" and "0" on IE
}

Juga, berbicara tentang properti yang diwarisi, jika Anda, misalnya, memperluas Array.prototypeobjek (seperti beberapa pustaka seperti yang dilakukan MooTools), properti itu juga akan disebutkan:

Array.prototype.last = function () { return this[this.length-1]; };

for (var p in []) { // an empty array
  // last will be enumerated
}

Seperti yang saya katakan sebelumnya untuk beralih pada array atau objek seperti array, hal terbaik adalah menggunakan loop berurutan , seperti plain-old for/ whileloop.

Saat Anda ingin menghitung hanya properti sendiri dari suatu objek (yang tidak diwariskan), Anda dapat menggunakan hasOwnPropertymetode ini:

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    // prop is not inherited
  }
}

Dan beberapa orang bahkan merekomendasikan untuk memanggil metode langsung dari Object.prototypeuntuk menghindari masalah jika seseorang menambahkan properti yang dinamai hasOwnPropertyke objek kita:

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }
}
CMS
sumber
10
Lihat juga postingan David Humphrey yang Iterating atas Object di JavaScript dengan cepat - karena array for..injauh lebih lambat daripada loop "normal".
Chris Morgan
17
Pertanyaan tentang poin terakhir tentang "hasOwnProperty": Jika seseorang menimpa "hasOwnProperty" pada suatu objek, Anda akan mengalami masalah. Tetapi bukankah Anda akan memiliki masalah yang sama jika seseorang menimpa "Object.prototype.hasOwnProperty"? Apa pun cara mereka mengacaukan Anda dan itu bukan tanggung jawab Anda, kan?
Scott Rippey
Anda bilang for..initu bukan praktik buruk, tapi bisa disalahgunakan. Sudahkah Anda mendapatkan contoh nyata praktik nyata yang baik di dunia, di mana Anda benar-benar ingin melihat semua properti objek termasuk properti yang diwarisi?
rjmunro
4
@ScottRippey: Jika Anda ingin membawanya ke sana: youtube.com/watch?v=FrFUI591WhI
Nathan Wall
dengan jawaban ini saya menemukan bahwa dapat mengakses nilai denganfor (var p in array) { array[p]; }
equiman
117

Ada tiga alasan mengapa Anda tidak perlu menggunakannya for..inuntuk mengulangi elemen array:

  • for..inakan mengulang semua properti yang dimiliki dan diwarisi dari objek array yang tidak DontEnum; itu berarti jika seseorang menambahkan properti ke objek array tertentu (ada alasan yang valid untuk ini - saya sudah melakukannya sendiri) atau berubah Array.prototype(yang dianggap praktik buruk dalam kode yang seharusnya bekerja dengan baik dengan skrip lain), properti ini akan diulangi juga; properti yang diwarisi dapat dikecualikan dengan memeriksa hasOwnProperty(), tetapi itu tidak akan membantu Anda dengan properti yang diatur dalam objek array itu sendiri

  • for..in tidak dijamin untuk mempertahankan pemesanan elemen

  • itu lambat karena Anda harus berjalan semua properti objek array dan seluruh rantai prototipe dan masih hanya akan mendapatkan nama properti, yaitu untuk mendapatkan nilai, pencarian tambahan akan diperlukan

Christoph
sumber
55

Karena untuk ... dalam penghitungan melalui objek yang menyimpan array, bukan array itu sendiri. Jika saya menambahkan fungsi ke rantai prototipe array, itu juga akan disertakan. Yaitu

Array.prototype.myOwnFunction = function() { alert(this); }
a = new Array();
a[0] = 'foo';
a[1] = 'bar';
for(x in a){
 document.write(x + ' = ' + a[x]);
}

Ini akan menulis:

0 = foo
1 = bar
myOwnFunction = function () {alert (this); }

Dan karena Anda tidak pernah bisa yakin bahwa tidak ada yang akan ditambahkan ke rantai prototipe cukup gunakan for loop untuk menyebutkan array:

for(i=0,x=a.length;i<x;i++){
 document.write(i + ' = ' + a[i]);
}

Ini akan menulis:

0 = foo
1 = bar
Pim Jager
sumber
16
Array adalah Objects, tidak ada "objek yang menyimpan array".
RobG
41

Secara terpisah, tidak ada salahnya menggunakan for-in pada array. For-in iterates atas nama properti dari suatu objek, dan dalam kasus array "out-of-the-box", properti sesuai dengan indeks array. (Propertes bawaan suka length, toStringdan sebagainya tidak termasuk dalam iterasi.)

Namun, jika kode Anda (atau kerangka yang Anda gunakan) menambahkan properti kustom ke array atau ke prototipe array, maka properti ini akan dimasukkan dalam iterasi, yang mungkin bukan yang Anda inginkan.

Beberapa kerangka kerja JS, seperti Prototipe memodifikasi prototipe Array. Kerangka kerja lain seperti JQuery tidak, jadi dengan JQuery Anda dapat menggunakan for-in dengan aman.

Jika Anda ragu, Anda sebaiknya tidak menggunakan for-in.

Cara alternatif untuk iterasi melalui array menggunakan for-loop:

for (var ix=0;ix<arr.length;ix++) alert(ix);

Namun, ini memiliki masalah yang berbeda. Masalahnya adalah bahwa array JavaScript dapat memiliki "lubang". Jika Anda mendefinisikan arrsebagai:

var arr = ["hello"];
arr[100] = "goodbye";

Kemudian array memiliki dua item, tetapi panjang 101. Menggunakan for-in akan menghasilkan dua indeks, sedangkan for-loop akan menghasilkan 101 indeks, di mana 99 memiliki nilai undefined.

JacquesB
sumber
37

Pada 2016 (ES6) kita dapat menggunakan for…ofuntuk iterasi array, seperti yang sudah diperhatikan oleh John Slegers.

Saya hanya ingin menambahkan kode demonstrasi sederhana ini, untuk membuatnya lebih jelas:

Array.prototype.foo = 1;
var arr = [];
arr[5] = "xyz";

console.log("for...of:");
var count = 0;
for (var item of arr) {
    console.log(count + ":", item);
    count++;
    }

console.log("for...in:");
count = 0;
for (var item in arr) {
    console.log(count + ":", item);
    count++;
    }

Konsol menunjukkan:

for...of:

0: undefined
1: undefined
2: undefined
3: undefined
4: undefined
5: xyz

for...in:

0: 5
1: foo

Dengan kata lain:

  • for...ofdihitung dari 0 hingga 5, dan juga diabaikan Array.prototype.foo. Ini menunjukkan nilai array .

  • for...inhanya mencantumkan 5, mengabaikan indeks array yang tidak ditentukan, tetapi menambahkan foo. Ini menunjukkan nama properti array .

MarcG
sumber
32

Jawaban singkat: Itu tidak layak.


Jawaban yang lebih panjang: Itu tidak layak, bahkan jika urutan elemen berurutan dan kinerja optimal tidak diperlukan.


Jawaban panjang: Tidak layak ...

  • Menggunakan for (var property in array)akan menyebabkan arrayuntuk diiterasi sebagai objek , melintasi rantai prototipe objek dan akhirnya melakukan lebih lambat daripada berbasis indeksfor loop .
  • for (... in ...) tidak dijamin untuk mengembalikan properti objek dalam urutan berurutan, seperti yang mungkin diharapkan.
  • Menggunakan hasOwnProperty()dan !isNaN()memeriksa untuk menyaring properti objek adalah overhead tambahan yang menyebabkannya bekerja lebih lambat dan meniadakan alasan utama untuk menggunakannya di tempat pertama, yaitu karena format yang lebih ringkas.

Untuk alasan ini, trade-off yang dapat diterima antara kinerja dan kenyamanan bahkan tidak ada. Benar-benar tidak ada manfaat kecuali tujuannya adalah untuk menangani array sebagai objek dan melakukan operasi pada properti objek array.

WynandB
sumber
31

Selain alasan yang diberikan dalam jawaban lain, Anda mungkin tidak ingin menggunakan struktur "untuk ... dalam" jika Anda perlu melakukan matematika dengan variabel penghitung karena loop berulang melalui nama properti objek dan variabel tersebut adalah sebuah string.

Sebagai contoh,

for (var i=0; i<a.length; i++) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

akan menulis

0, number, 1
1, number, 2
...

sedangkan,

for (var ii in a) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

akan menulis

0, string, 01
1, string, 11
...

Tentu saja, ini dapat dengan mudah diatasi dengan memasukkan

ii = parseInt(ii);

dalam lingkaran, tetapi struktur pertama lebih langsung.

ctmiddle
sumber
6
Anda dapat menggunakan awalan +alih-alih parseIntkecuali Anda benar-benar membutuhkan integer atau mengabaikan karakter yang tidak valid.
Konrad Borowski
Juga, menggunakan parseInt()tidak dianjurkan. Coba parseInt("025");dan itu akan gagal.
Derek 朕 會 功夫
6
@Derek 朕 會 功夫 - Anda pasti bisa menggunakan parseInt. Masalahnya adalah jika Anda tidak memasukkan radix, browser yang lebih lama mungkin mencoba menginterpretasikan angka (sehingga 025 menjadi oktal). Ini diperbaiki dalam ECMAScript 5 tetapi masih terjadi untuk angka yang dimulai dengan "0x" (itu menafsirkan angka sebagai hex). Untuk berada di sisi yang aman, gunakan radix untuk menentukan nomor seperti parseInt("025", 10)- yang menentukan basis 10.
IAmTimCorey
23

Selain dari fakta bahwa for... inloop atas semua properti yang dapat dihitung (yang tidak sama dengan "semua elemen array"!), Lihat http://www.ecma-international.org/publications/files/ECMA-ST/Ecma -262.pdf , bagian 12.6.4 (edisi ke-5) atau 13.7.5.15 (edisi ke-7):

Mekanika dan urutan penghitungan properti ... tidak ditentukan ...

(Penekanan milikku.)

Itu berarti jika suatu browser ingin, itu bisa melalui properti dalam urutan di mana mereka dimasukkan. Atau dalam urutan numerik. Atau dalam urutan leksikal (di mana "30" muncul sebelum "4"! Ingat semua kunci objek - dan dengan demikian, semua indeks array - sebenarnya adalah string, sehingga masuk akal secara total). Itu bisa melalui mereka dengan ember, jika mengimplementasikan objek sebagai tabel hash. Atau ambil salah satu dari itu dan tambahkan "mundur". Peramban bahkan dapat beralih secara acak dan memenuhi persyaratan ECMA-262, asalkan mengunjungi setiap properti tepat satu kali.

Dalam praktiknya, sebagian besar browser saat ini ingin beralih dalam urutan yang kurang lebih sama. Tapi tidak ada yang mengatakan mereka harus melakukannya. Itu implementasi spesifik, dan bisa berubah kapan saja jika cara lain ditemukan jauh lebih efisien.

Either way, for... indisertai dengan itu tidak ada konotasi ketertiban. Jika Anda peduli tentang pesanan, jelaskan tentang hal itu dan gunakan forloop reguler dengan indeks.

cao
sumber
18

Terutama dua alasan:

Satu

Seperti yang dikatakan orang lain, Anda mungkin mendapatkan kunci yang tidak ada dalam array Anda atau yang diwarisi dari prototipe. Jadi jika, katakanlah, perpustakaan menambahkan properti ke prototipe Array atau Object:

Array.prototype.someProperty = true

Anda akan mendapatkannya sebagai bagian dari setiap larik:

for(var item in [1,2,3]){
  console.log(item) // will log 1,2,3 but also "someProperty"
}

Anda bisa menyelesaikan ini dengan metode hasOwnProperty:

var ary = [1,2,3];
for(var item in ary){
   if(ary.hasOwnProperty(item)){
      console.log(item) // will log only 1,2,3
   }
}

tapi ini berlaku untuk iterasi atas objek apa pun dengan loop for-in.

Dua

Biasanya urutan item dalam array adalah penting, tetapi loop for-in tidak harus berulang dalam urutan yang benar, itu karena ia memperlakukan array sebagai objek, yang merupakan cara penerapannya dalam JS, dan tidak sebagai sebuah array. Ini sepertinya hal kecil, tetapi benar-benar dapat mengacaukan aplikasi dan sulit untuk di-debug.

Lior
sumber
2
Object.keys(a).forEach( function(item) { console.log(item) } )beralih di atas array kunci properti sendiri, bukan yang diwarisi dari prototipe.
Qwerty
2
Benar, tetapi seperti for-in loop, itu tidak selalu berada dalam urutan indeks yang benar. Juga, itu tidak akan berfungsi pada browser lama yang tidak mendukung ES5.
Lior
Anda dapat mengajarkan browser tersebut array.forEachdengan memasukkan kode tertentu ke dalam skrip Anda. Lihat Polyfill developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Qwerty
Tentu saja, tetapi kemudian Anda memanipulasi prototipe, dan itu tidak selalu merupakan ide yang baik ... dan masih, Anda memiliki masalah urutan ...
Lior
Dan, tentu saja, alasan nomor tiga: Jarang array.
oliver yang lebih baik pada
16

Karena ia menyebutkan melalui bidang objek, bukan indeks. Anda bisa mendapatkan nilai dengan indeks "panjang" dan saya ragu Anda menginginkannya.

vava
sumber
Jadi apa cara terbaik untuk melakukan itu?
lYriCAlsSH
3
for (var i = 0; i <arr.length; i ++) {}
vava
3
Di firefox 3 Anda juga bisa menggunakan arr.forEach atau untuk (var [i, v] di Iterator (arr)) {} tetapi tidak ada yang bekerja di IE, meskipun Anda bisa menulis sendiri metode forEach.
vava
dan hampir setiap perpustakaan memiliki metode sendiri untuk ini juga.
vava
5
Jawaban ini salah. "Panjang" tidak akan termasuk dalam iterasi untuk-dalam. Hanya properti yang Anda tambahkan sendiri yang disertakan.
JacquesB
16

Saya rasa saya tidak punya banyak hal untuk ditambahkan misalnya. Jawaban Triptych atau jawaban CMS tentang mengapa menggunakanfor...in harus dihindari dalam beberapa kasus.

Namun, saya ingin menambahkan bahwa di browser modern ada alternatif for...inyang dapat digunakan dalam kasus-kasus di mana for...intidak dapat digunakan. Alternatif itu adalah for...of:

for (var item of items) {
    console.log(item);
}

Catatan :

Sayangnya, tidak ada versi Internet Explorer yang mendukung for...of( Edge 12+ melakukannya), jadi Anda harus menunggu sedikit lebih lama hingga Anda dapat menggunakannya dalam kode produksi sisi klien Anda. Namun, aman untuk digunakan di kode JS sisi server Anda (jika Anda menggunakan Node.js ).

John Slegers
sumber
@georgeawg Maksudmu for-of, bukan for-in?
ᆼ ᆺ ᆼ
15

Masalah dengan for ... in ...- dan ini hanya menjadi masalah ketika seorang programmer tidak benar-benar mengerti bahasa; itu bukan benar-benar bug atau apa pun - adalah bahwa itu berulang atas semua anggota objek (well, semua anggota enumerable , tapi itu detail untuk saat ini). Ketika Anda ingin beralih hanya pada properti yang diindeks dari array, satu-satunya cara dijamin untuk menjaga hal-hal konsisten secara semantik adalah dengan menggunakan indeks integer (yaitu, for (var i = 0; i < array.length; ++i)loop gaya).

Objek apa pun dapat memiliki sifat sewenang-wenang yang terkait dengannya. Tidak ada yang buruk tentang memuat properti tambahan ke instance array, khususnya. Kode yang ingin melihat hanya properti seperti array yang diindeks karena itu harus tetap berpegang pada indeks integer. Kode yang sepenuhnya menyadari apa yang perlufor ... in dan benar-benar perlu untuk melihat semua properti, baik maka itu juga ok.

Runcing
sumber
Penjelasan bagus Pointy. Hanya penasaran. Jika saya memiliki array yang ada di dalam objek di bawah properti multiply dan apakah for in, dibandingkan dengan reguler untuk loop, array tersebut akan diulang? (yang pada intinya, kinerja lambat, kan?)
NiCk Newman
2
@NiCkNewman juga objek yang Anda referensi setelah indalam satu for ... inlingkaran hanya akan
Runcing
Saya melihat. Hanya ingin tahu karena saya memiliki objek dan array di dalam objek permainan utama saya dan bertanya-tanya apakah untuk inning itu akan lebih menyakitkan maka cukup biasa untuk loop pada indeks.
NiCk Newman
@NiCkNewman baik tema dari seluruh pertanyaan ini adalah bahwa Anda tidak boleh menggunakan for ... inpada array; ada banyak alasan bagus untuk tidak melakukannya. Ini bukan masalah kinerja seperti masalah "pastikan tidak rusak".
Runcing
Nah, objek saya disimpan dalam array secara teknis, itu sebabnya saya khawatir, sesuatu seperti [{a:'hey',b:'hi'},{a:'hey',b:'hi'}]:, tapi ya, saya mengerti.
NiCk Newman
9

Juga, karena semantik, cara for, inmemperlakukan array (yaitu sama dengan objek JavaScript lainnya) tidak selaras dengan bahasa populer lainnya.

// C#
char[] a = new char[] {'A', 'B', 'C'};
foreach (char x in a) System.Console.Write(x); //Output: "ABC"

// Java
char[] a = {'A', 'B', 'C'};
for (char x : a) System.out.print(x);          //Output: "ABC"

// PHP
$a = array('A', 'B', 'C');
foreach ($a as $x) echo $x;                    //Output: "ABC"

// JavaScript
var a = ['A', 'B', 'C'];
for (var x in a) document.write(x);            //Output: "012"
matpop
sumber
9

TL&DR: Menggunakan for inloop dalam array bukanlah kejahatan, pada kenyataannya justru sebaliknya.

Saya pikir for inloop adalah permata dari JS jika digunakan dengan benar dalam array. Anda diharapkan memiliki kontrol penuh atas perangkat lunak Anda dan tahu apa yang Anda lakukan. Mari kita lihat kekurangan yang disebutkan dan buktikan satu per satu.

  1. Itu loop melalui properti yang diwarisi juga: Pertama-tama semua ekstensi ke yang Array.prototypeseharusnya telah dilakukan dengan menggunakan Object.defineProperty()dan enumerabledeskriptor mereka harus diatur ke false. Pustaka yang tidak melakukannya tidak boleh digunakan sama sekali.
  2. Properti-properti yang Anda tambahkan ke rantai warisan kemudian mendapatkan dihitung: Ketika melakukan berbagai sub-mengklasifikasikan oleh Object.setPrototypeOfatau dengan Kelas extend. Anda harus menggunakan lagi Object.defineProperty()yang secara default mengatur writable, enumerabledan configurabledeskriptor properti false. Mari kita lihat contoh sub-klasifikasi array di sini ...

function Stack(...a){
  var stack = new Array(...a);
  Object.setPrototypeOf(stack, Stack.prototype);
  return stack;
}
Stack.prototype = Object.create(Array.prototype);                                 // now stack has full access to array methods.
Object.defineProperty(Stack.prototype,"constructor",{value:Stack});               // now Stack is a proper constructor
Object.defineProperty(Stack.prototype,"peak",{value: function(){                  // add Stack "only" methods to the Stack.prototype.
                                                       return this[this.length-1];
                                                     }
                                             });
var s = new Stack(1,2,3,4,1);
console.log(s.peak());
s[s.length] = 7;
console.log("length:",s.length);
s.push(42);
console.log(JSON.stringify(s));
console.log("length:",s.length);

for(var i in s) console.log(s[i]);

Jadi Anda tahu .. for inloop sekarang aman karena Anda peduli dengan kode Anda.

  1. The for inLoop lambat: Hell no. Ini sejauh ini metode iterasi tercepat jika Anda mengulang array jarang yang diperlukan dari waktu ke waktu. Ini adalah salah satu trik kinerja paling penting yang harus diketahui. Mari kita lihat sebuah contoh. Kami akan mengulangi array yang jarang.

var a = [];
a[0] = "zero";
a[10000000] = "ten million";
console.time("for loop on array a:");
for(var i=0; i < a.length; i++) a[i] && console.log(a[i]);
console.timeEnd("for loop on array a:");
console.time("for in loop on array a:");
for(var i in a) a[i] && console.log(a[i]);
console.timeEnd("for in loop on array a:");

Redu
sumber
Pengaturan pembandingan @Ravi Shanker Reddy Good. Seperti yang telah saya sebutkan dalam jawaban saya, for inloop mengungguli yang lain "jika" array jarang dan semakin banyak jika semakin besar ukurannya. Jadi saya telah mengatur ulang tes bangku untuk array jarang arr,, dari ukuran ~ 10.000 dengan hanya 50 item yang dipilih secara [42,"test",{t:1},null, void 0]acak di antara indeks acak. Anda akan segera melihat perbedaannya. - >> Periksa di sini << - .
Redu
8

Selain masalah lain, sintaks "for..in" mungkin lebih lambat, karena indeksnya adalah string, bukan bilangan bulat.

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'
dc1
sumber
Mungkin tidak terlalu penting. Elemen array adalah properti dari objek berbasis Array atau seperti Array, dan semua properti objek memiliki kunci string. Kecuali jika mesin JS Anda mengoptimalkannya, bahkan jika Anda menggunakan nomor, akhirnya akan diubah menjadi string untuk pencarian.
cHao
Terlepas dari masalah kinerja apa pun, jika Anda baru mengenal JavaScript, gunakan var i in adan perkirakan indeksnya adalah bilangan bulat, maka melakukan sesuatu seperti a[i+offset] = <value>akan meletakkan nilai di tempat yang sepenuhnya salah. ("1" + 1 == "11").
szmoore
8

Aspek penting adalah bahwa for...inhanya iterate atas properti yang terkandung dalam objek yang atribut enumerable properti mereka disetel ke true. Jadi, jika seseorang mencoba untuk mengulangi objek menggunakan for...inmaka properti sewenang-wenang dapat dilewatkan jika atribut properti enumerable mereka salah. Sangat mungkin untuk mengubah atribut properti enumerable untuk objek Array normal sehingga elemen-elemen tertentu tidak disebutkan. Meskipun secara umum atribut properti cenderung berlaku untuk fungsi properti dalam suatu objek.

Orang dapat memeriksa nilai atribut properti enumerable properti dengan:

myobject.propertyIsEnumerable('myproperty')

Atau untuk memperoleh keempat atribut properti:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

Ini adalah fitur yang tersedia di ECMAScript 5 - dalam versi sebelumnya tidak mungkin mengubah nilai atribut properti enumerable (selalu disetel ke true).

Pierz
sumber
8

The for/ inbekerja dengan dua jenis variabel: hashtable (array asosiatif) dan array (non-asosiatif).

JavaScript akan secara otomatis menentukan cara barang tersebut melewati item. Jadi, jika Anda tahu bahwa array Anda benar-benar non-asosiatif, Anda dapat menggunakan for (var i=0; i<=arrayLen; i++), dan melewatkan iterasi deteksi otomatis.

Tetapi menurut saya, lebih baik menggunakan for/ in, proses yang diperlukan untuk deteksi otomatis itu sangat kecil.

Jawaban nyata untuk ini akan tergantung pada bagaimana parser / penterjemah kode JavaScript. Itu dapat berubah di antara browser.

Saya tidak dapat memikirkan tujuan lain untuk tidak menggunakan for/ in;

//Non-associative
var arr = ['a', 'b', 'c'];
for (var i in arr)
   alert(arr[i]);

//Associative
var arr = {
   item1 : 'a',
   item2 : 'b',
   item3 : 'c'
};

for (var i in arr)
   alert(arr[i]);
Ricardo
sumber
benar, kecuali jika Anda menggunakan objek prototipe. ;) di bawah ini
Ricardo
Karena thats Arrayadalah Objectterlalu
Gratis Consulting
2
for ... inbekerja dengan benda. Tidak ada yang namanya deteksi otomatis.
oliver yang lebih baik pada
7

Karena itu akan beralih pada properti yang dimiliki objek hingga rantai prototipe jika Anda tidak hati-hati.

Anda dapat menggunakan for.. in, pastikan untuk memeriksa setiap properti dengan hasOwnProperty .

JAL
sumber
2
Tidak cukup - tidak apa-apa untuk menambahkan properti yang diberi nama arbitrer ke instance array, dan itu akan diuji truedari hasOwnProperty()cek.
Runcing
Poin bagus, terima kasih. Aku tidak pernah sebodoh itu untuk melakukan itu pada Array sendiri, jadi aku belum mempertimbangkan itu!
JAL
1
@Pointy Saya belum menguji ini, tapi mungkin ini bisa diatasi dengan menggunakan isNaNcek pada setiap nama properti.
WynandB
1
@ Menangkan ide yang menarik; Namun saya tidak benar-benar melihat mengapa itu sepadan dengan masalah ketika iterasi dengan indeks numerik sederhana sangat mudah.
Runcing
@ WynandB maaf untuk benjolan, tapi saya merasa ada koreksi: isNaNuntuk memeriksa apakah suatu variabel adalah nilai khusus NaN atau tidak, itu tidak dapat digunakan untuk memeriksa 'hal-hal selain angka' (Anda dapat menggunakan reguler typeof untuk itu).
doldt
6

Ini tidak selalu buruk (berdasarkan apa yang Anda lakukan), tetapi dalam kasus array, jika sesuatu telah ditambahkan Array.prototype, maka Anda akan mendapatkan hasil yang aneh. Di mana Anda mengharapkan loop ini berjalan tiga kali:

var arr = ['a','b','c'];
for (var key in arr) { ... }

Jika fungsi yang disebut helpfulUtilityMethodtelah ditambahkan ke Array's prototype, maka loop Anda akan berakhir berjalan empat kali: keyakan 0, 1, 2, dan helpfulUtilityMethod. Jika Anda hanya mengharapkan bilangan bulat, oops.

josh3736
sumber
6

Anda harus menggunakan for(var x in y)hanya pada daftar properti, bukan pada objek (seperti yang dijelaskan di atas).

pengguna268396
sumber
13
Hanya catatan tentang SO - tidak ada 'di atas' karena komentar mengubah urutan halaman setiap saat. Jadi, kami tidak benar-benar tahu komentar yang Anda maksud. Adalah baik untuk mengatakan "dalam komentar x orang" karena alasan ini.
JAL
@ JAL ... atau tambahkan tautan permanen ke jawabannya.
WynandB
5

Menggunakan for...inloop untuk sebuah array tidak salah, walaupun saya bisa menebak mengapa seseorang memberi tahu Anda bahwa:

1.) Sudah ada fungsi orde yang lebih tinggi, atau metode, yang memiliki tujuan untuk array, tetapi memiliki lebih banyak fungsi dan sintaks yang lebih ramping, yang disebut 'forEach': Array.prototype.forEach(function(element, index, array) {} );

2.) Array selalu memiliki panjang, tetapi for...indan forEachtidak menjalankan fungsi untuk nilai apa pun 'undefined', hanya untuk indeks yang memiliki nilai yang ditentukan. Jadi jika Anda hanya menetapkan satu nilai, loop ini hanya akan mengeksekusi fungsi sekali, tetapi karena array dihitung, itu akan selalu memiliki panjang hingga indeks tertinggi yang memiliki nilai yang ditentukan, tetapi panjang itu bisa luput dari perhatian ketika menggunakan ini loop.

3.) Standar untuk loop akan menjalankan fungsi sebanyak yang Anda tentukan dalam parameter, dan karena array diberi nomor, lebih masuk akal untuk menentukan berapa kali Anda ingin menjalankan suatu fungsi. Tidak seperti loop lainnya, loop for kemudian dapat menjalankan fungsi untuk setiap indeks dalam array, apakah nilainya didefinisikan atau tidak.

Intinya, Anda bisa menggunakan loop apa saja, tetapi Anda harus ingat persis cara kerjanya. Memahami kondisi di mana loop berbeda mengulangi, fungsi terpisah mereka, dan menyadari bahwa mereka akan lebih atau kurang sesuai untuk skenario yang berbeda.

Juga, mungkin dianggap sebagai praktik yang lebih baik untuk menggunakan forEachmetode ini daripada for...inloop pada umumnya, karena lebih mudah untuk menulis dan memiliki lebih banyak fungsi, jadi Anda mungkin ingin membiasakan diri hanya menggunakan metode dan standar ini untuk, tetapi Anda panggilan.

Lihat di bawah ini bahwa dua loop pertama hanya menjalankan pernyataan console.log sekali, sedangkan standar untuk loop mengeksekusi fungsi sebanyak yang ditentukan, dalam hal ini, array.length = 6.

var arr = [];
arr[5] = 'F';

for (var index in arr) {
console.log(index);
console.log(arr[index]);
console.log(arr)
}
// 5
// 'F'
// => (6) [undefined x 5, 6]

arr.forEach(function(element, index, arr) {
console.log(index);
console.log(element);
console.log(arr);
});
// 5
// 'F'
// => Array (6) [undefined x 5, 6]

for (var index = 0; index < arr.length; index++) {
console.log(index);
console.log(arr[index]);
console.log(arr);
};
// 0
// undefined
// => Array (6) [undefined x 5, 6]

// 1
// undefined
// => Array (6) [undefined x 5, 6]

// 2
// undefined
// => Array (6) [undefined x 5, 6]

// 3
// undefined
// => Array (6) [undefined x 5, 6]

// 4
// undefined
// => Array (6) [undefined x 5, 6]

// 5
// 'F'
// => Array (6) [undefined x 5, 6]
mrmaclean89
sumber
4

Berikut adalah alasan mengapa ini (biasanya) merupakan praktik buruk:

  1. for...inloop mengulangi semua properti enumerable mereka sendiri dan properti enumerable prototipe mereka. Biasanya dalam iterasi array kita hanya ingin mengulangi array itu sendiri. Dan meskipun Anda sendiri mungkin tidak menambahkan apa pun ke array, pustaka atau kerangka kerja Anda mungkin menambahkan sesuatu.

Contoh :

Array.prototype.hithere = 'hithere';

var array = [1, 2, 3];
for (let el in array){
    // the hithere property will also be iterated over
    console.log(el);
}

  1. for...inloop tidak menjamin urutan iterasi tertentu . Meskipun pesanan biasanya terlihat di sebagian besar browser modern hari ini, masih belum ada jaminan 100%.
  2. for...inloop mengabaikan undefinedelemen array, yaitu elemen array yang belum ditetapkan.

Contoh ::

const arr = []; 
arr[3] = 'foo';   // resize the array to 4
arr[4] = undefined; // add another element with value undefined to it

// iterate over the array, a for loop does show the undefined elements
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

console.log('\n');

// for in does ignore the undefined elements
for (let el in arr) {
    console.log(arr[el]);
}

Willem van der Veen
sumber
2

untuk ... di berguna ketika bekerja pada objek dalam JavaScript, tetapi tidak untuk Array, tapi tetap saja kita tidak bisa mengatakan itu cara yang salah, tetapi tidak disarankan, lihat contoh di bawah ini menggunakan for ... in loop:

let txt = "";
const person = {fname:"Alireza", lname:"Dezfoolian", age:35}; 
for (const x in person) {
    txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

OK, mari kita lakukan dengan Array sekarang:

let txt = "";
const person = ["Alireza", "Dezfoolian", 35]; 
for (const x in person) {
   txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

Seperti yang Anda lihat hasilnya sama ...

Tapi mari kita coba sesuatu, mari kita prototipe sesuatu ke Array ...

Array.prototype.someoneelse = "someoneelse";

Sekarang kita membuat Array baru ();

let txt = "";
const arr = new Array();
arr[0] = 'Alireza';
arr[1] = 'Dezfoolian';
arr[2] = 35;
for(x in arr) {
 txt += arr[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 someoneelse

Kalian lihat seseorang itu sendiri !!! ... Kita benar-benar mengulang-ulang objek Array baru dalam kasus ini!

Jadi itulah salah satu alasan mengapa kita perlu menggunakan untuk..dalam hati-hati, tapi itu tidak selalu terjadi ...

Alireza
sumber
2

A for ... in loop selalu menyebutkan kunci. Kunci properti objek selalu berupa String, bahkan properti yang diindeks dari sebuah array:

var myArray = ['a', 'b', 'c', 'd'];
var total = 0
for (elem in myArray) {
  total += elem
}
console.log(total); // 00123
Maher Tliba
sumber
0

walaupun tidak secara khusus ditanggapi oleh pertanyaan ini, saya akan menambahkan bahwa ada alasan yang sangat baik untuk tidak pernah menggunakan ... dengan NodeList(karena orang akan mendapatkan dari querySelectorAllpanggilan, karena tidak melihat elemen yang dikembalikan sama sekali, sebagai gantinya hanya mengulangi properti NodeList.

dalam hal hasil tunggal, saya mendapat:

var nodes = document.querySelectorAll(selector);
nodes
 NodeList [a._19eb]
for (node in nodes) {console.log(node)};
VM505:1 0
VM505:1 length
VM505:1 item
VM505:1 entries
VM505:1 forEach
VM505:1 keys
VM505:1 values

yang menjelaskan mengapa saya for (node in nodes) node.href = newLink;gagal.

jcomeau_ictx
sumber