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


Saya sudah diberitahu untuk tidak menggunakannya for...in dengan array dalam JavaScript. Kenapa tidak?


1539
2018-02-01 09:46


asal


Jawaban:


Alasannya adalah salah satu konstruk:

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
*/

terkadang 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 itu JavaScript pustaka mungkin melakukan hal-hal seperti ini, yang akan memengaruhi larik 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
*/


1341
2018-02-01 10:08



Itu for-in Pernyataan itu sendiri bukanlah "praktik yang buruk", tetapi bisa juga salah digunakan, misalnya, ke iterate lebih dari array atau objek seperti array.

Tujuan dari for-in Pernyataan adalah untuk menghitung lebih dari properti objek. Pernyataan ini akan naik dalam rantai prototipe, juga disebutkan di atas diwariskan properti, hal itu terkadang tidak diinginkan.

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

Sebagai contoh, dalam JScript (IE <= 8), urutan pencacahan bahkan pada objek Array didefinisikan sebagai properti diciptakan:

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 diwariskan, jika Anda, misalnya, memperpanjang Array.prototype objek (seperti beberapa perpustakaan seperti MooTools lakukan), properti yang akan juga 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 iterate lebih dari array atau objek seperti array, yang terbaik adalah menggunakan loop berurutan, seperti biasa-biasa saja for/while lingkaran.

Ketika Anda ingin menyebutkan hanya memiliki properti sendiri dari suatu objek (yang tidak diwariskan), Anda dapat menggunakan hasOwnProperty metode:

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

Dan beberapa orang bahkan menyarankan untuk memanggil metode langsung dari Object.prototype untuk menghindari masalah jika seseorang menambahkan properti bernama hasOwnProperty ke objek kami:

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

356
2017-11-23 21:22



Ada tiga alasan mengapa Anda tidak harus menggunakannya for..in untuk mengulang elemen array:

  • for..in akan menggilir 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 sah untuk ini - saya telah melakukannya sendiri) atau diubah Array.prototype (yang dianggap praktik buruk dalam kode yang seharusnya bekerja dengan baik dengan skrip lain), properti ini akan diulang juga; sifat yang diwariskan 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 dari objek array dan seluruh rantai prototipe dan masih akan hanya mendapatkan nama properti, yaitu untuk mendapatkan nilai, pencarian tambahan akan diperlukan


101
2018-02-01 14:04



Karena untuk ... di enumerates 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 (ini); }

Dan karena Anda tidak pernah bisa memastikan bahwa tidak ada yang akan ditambahkan ke rantai prototipe, cukup gunakan loop untuk menghitung larik:

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

Ini akan menulis:

0 = foo
1 = bar

48
2018-02-01 10:08



Dalam isolasi, tidak ada yang salah dengan menggunakan for-in pada array. For-in iterates atas nama properti suatu objek, dan dalam kasus array "out-of-the-box", properti sesuai dengan indeks array. (The built-in seperti propertes length, toString dan seterusnya tidak termasuk dalam iterasi.)

Namun, jika kode Anda (atau kerangka kerja 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 JS, seperti Prototipe memodifikasi prototipe Array. Kerangka lain seperti JQuery tidak, jadi dengan JQuery Anda dapat menggunakan for-in dengan aman.

Jika Anda ragu, Anda mungkin sebaiknya tidak menggunakan untuk-masuk.

Cara alternatif 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 susunan JavaScript dapat memiliki "lubang". Jika Anda mendefinisikan arr sebagai:

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

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


37
2018-02-01 10:34



Selain alasan yang diberikan dalam jawaban lain, Anda mungkin tidak ingin menggunakan struktur "untuk ... dalam" jika Anda perlu melakukan matematika dengan variabel counter karena loop iterates melalui nama-nama sifat objek dan variabel 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.


28
2017-09-02 02:29



Mulai 2016 (ES6) yang dapat kami gunakan for…of untuk iterasi array, seperti John Slegers sudah perhatikan.

Saya hanya ingin menambahkan kode demonstrasi sederhana ini, untuk membuat semuanya 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...of dihitung dari 0 hingga 5, dan juga mengabaikan Array.prototype.foo. Ini menunjukkan array nilai-nilai.

  • for...in daftar hanya 5, mengabaikan indeks array tidak terdefinisi, tetapi menambahkan foo. Ini menunjukkan array nama properti.


24
2018-03-10 04:29