Apa artinya JSLint error 'body of for in harus dibungkus dengan pernyataan if'?

242

Saya menggunakan JSLint pada file JavaScript milik saya. Itu melemparkan kesalahan:

for( ind in evtListeners ) {

Masalah pada baris 41 karakter 9: Tubuh for in harus dibungkus dengan pernyataan if untuk menyaring properti yang tidak diinginkan dari prototipe.

Apa artinya ini?

jrharshath
sumber
5
Secara default 'in' mengulangi properti yang diwarisi juga. Biasanya, bodi dibungkus if (evtListeners.hasOwnProperty(ind))untuk membatasi pemrosesan hanya untuk memiliki properti (tidak diwariskan). Namun, dalam beberapa kasus Anda benar-benar ingin mengulang semua properti, termasuk yang diwarisi. Dalam hal itu, JSLint memaksa Anda untuk membungkus loop body dalam pernyataan if untuk memutuskan properti mana yang benar-benar Anda inginkan. Ini akan berhasil dan membuat JSlint senang: if (evtListeners[ind] !== undefined)
xorcus
1
Sebagian besar jawaban sudah usang. solusi yang diperbarui dapat ditemukan di stackoverflow.com/a/10167931/3138375
eli-bd

Jawaban:

430

Pertama-tama, jangan pernah menggunakan for inloop untuk menghitung lebih dari satu array. Tidak pernah. Pakai tua yang bagus for(var i = 0; i<arr.length; i++).

Alasan di balik ini adalah sebagai berikut: setiap objek dalam JavaScript memiliki bidang khusus yang disebut prototype. Segala sesuatu yang Anda tambahkan ke bidang itu akan dapat diakses pada setiap objek jenis itu. Misalkan Anda ingin semua array memiliki fungsi baru yang keren yang disebut filter_0yang akan menyaring nol.

Array.prototype.filter_0 = function() {
    var res = [];
    for (var i = 0; i < this.length; i++) {
        if (this[i] != 0) {
            res.push(this[i]);
        }
    }
    return res;
};

console.log([0, 5, 0, 3, 0, 1, 0].filter_0());
//prints [5,3,1]

Ini adalah cara standar untuk memperluas objek dan menambahkan metode baru. Banyak perpustakaan melakukan ini. Namun, mari kita lihat cara for inkerjanya sekarang:

var listeners = ["a", "b", "c"];
for (o in listeners) {
    console.log(o);
}
//prints:
//  0
//  1
//  2
//  filter_0

Apakah kamu lihat? Tiba-tiba berpikir filter_0 adalah indeks array lain. Tentu saja, ini bukan benar-benar indeks numerik, tetapi for inmenghitung melalui bidang objek, bukan hanya indeks numerik. Jadi kita sekarang menghitung melalui setiap indeks numerik dan filter_0 . Tetapi filter_0bukan bidang objek array tertentu, setiap objek array memiliki properti ini sekarang.

Untungnya, semua objek memiliki hasOwnPropertymetode, yang memeriksa apakah bidang ini benar-benar milik objek itu sendiri atau jika itu hanya diwarisi dari rantai prototipe dan dengan demikian milik semua objek jenis itu.

for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}
 //prints:
 //  0
 //  1
 //  2

Perhatikan, bahwa meskipun kode ini berfungsi seperti yang diharapkan untuk array, Anda tidak boleh, tidak pernah , menggunakan for indan for each inuntuk array. Ingat bahwa for inmenghitung bidang objek, bukan indeks array atau nilai.

var listeners = ["a", "b", "c"];
listeners.happy = "Happy debugging";

for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}

 //prints:
 //  0
 //  1
 //  2
 //  happy
vava
sumber
43
Anda seharusnya tidak menggunakan for inuntuk mengulangi array karena bahasa tidak akan mengurutkan urutan yang for inakan disebutkan lebih dari satu array. Mungkin tidak dalam urutan angka. Selain itu, jika Anda menggunakan konstruksi gaya `for (i = 0; i <array.length; i ++), Anda dapat yakin bahwa Anda hanya mengulangi indeks numerik secara berurutan, dan tidak ada properti alfanumerik.
Breton
Terima kasih! Saya akan menyimpan ini sebagai referensi.
nyuszika7h
Saya menemukan diri saya melihat jawaban ini lagi karena saya yakin bahwa sedikit JSLint ini rusak. Saya punya kode yang kira-kira: untuk (o dalam pendengar) {if (listeners.hasOwnProperty (i)) {console.log (o); }} Masalahnya adalah saya memiliki bug, saya mengganti nama variabel i menjadi o dan melewatkan referensi. JSLint cukup pintar untuk memastikan bahwa Anda memeriksa hasOwnProperty untuk properti yang benar pada objek yang benar.
drewish
12
karena di, bagaimanapun, baik untuk beralih di atas properti objek. OP tidak pernah mengatakan for in diterapkan pada Array. HasOwnProperty adalah praktik terbaik, namun ada beberapa kasus di mana Anda tidak menginginkannya - misalnya jika suatu objek meluas yang lain, dan Anda ingin membuat daftar objek dan properti 'induk' seseorang.
Gotofritz
3
Saya berpikir bahwa alih-alih menakut-nakuti orang dari for-inlingkaran (yang luar biasa, omong-omong), kita harus mendidik mereka bagaimana mereka bekerja (dilakukan dengan benar dalam jawaban ini) dan memperkenalkan mereka Object.defineProperty()sehingga mereka dapat dengan aman memperpanjang prototipe mereka tanpa merusak apa pun. Kebetulan, memperluas prototipe objek asli tidak boleh dilakukan tanpa Object.defineProperty.
Robert Rossmann
87

Douglas Crockford, penulis jslint telah menulis (dan berbicara) tentang masalah ini berkali-kali. Ada bagian pada ini halaman situs Web-nya yang meliputi ini:

untuk Pernyataan

A untuk kelas pernyataan harus memiliki bentuk berikut:

for (initialization; condition; update) {
    statements
}

for (variable in object) {
    if (filter) {
        statements
    } 
}

Bentuk pertama harus digunakan dengan array dan dengan loop dari jumlah iterasi yang tidak dapat ditentukan.

Bentuk kedua harus digunakan dengan objek. Sadarilah bahwa anggota yang ditambahkan ke prototipe objek akan dimasukkan dalam enumerasi. Adalah bijaksana untuk memprogram pertahanan dengan menggunakan metode hasOwnProperty untuk membedakan anggota objek yang sebenarnya:

for (variable in object) {
    if (object.hasOwnProperty(variable)) {
        statements
    } 
}

Crockford juga memiliki seri video tentang teater YUI di mana ia berbicara tentang ini. Seri video / pembicaraan Crockford tentang javascript harus dilihat jika Anda sedikit serius tentang javascript.

Breton
sumber
21

Buruk: (jsHint akan melempar kesalahan)

for (var name in item) {
    console.log(item[name]);
}

Baik:

for (var name in item) {
  if (item.hasOwnProperty(name)) {
    console.log(item[name]);
  }
}
Taro Alan
sumber
8

Jawaban Vava ada di sasaran. Jika Anda menggunakan jQuery, maka $.each()fungsinya menangani ini, maka itu lebih aman untuk digunakan.

$.each(evtListeners, function(index, elem) {
    // your code
});
HRJ
sumber
5
Jika kinerja menjadi pertimbangan di sini, saya tidak akan merekomendasikan menggunakan $.each(atau underscore.js _.each) jika Anda bisa lolos dengan forloop mentah . jsperf memiliki beberapa tes perbandingan yang membuka mata yang layak dijalankan.
nickb
3
Ini ( jsperf.com/each-vs-each-vs-for-in/3 ) lebih realistis karena menggunakan filter proto dasar
dvdrtrgn
7

@semua - semua yang ada di JavaScript adalah objek (), jadi pernyataan seperti "hanya gunakan ini pada objek" agak menyesatkan. Selain itu, JavaScript tidak diketik dengan kuat sehingga 1 == "1" benar (walaupun 1 === "1" tidak, Crockford besar dalam hal ini). Ketika datang ke konsep array progromatik di JS, mengetik penting dalam definisi.

@Brenton - Tidak perlu menjadi diktator terminologi; "array asosiatif", "kamus", "hash", "objek", semua konsep pemrograman ini berlaku untuk satu struktur di JS. Ini adalah pasangan nilai nama (kunci, indeks), di mana nilainya dapat berupa objek lain (string juga objek)

Jadi, new Array()sama dengan[]

new Object() kira-kira mirip dengan {}

var myarray = [];

Membuat struktur yang merupakan array dengan batasan bahwa semua indeks (alias kunci) harus berupa bilangan bulat. Ini juga memungkinkan untuk penetapan otomatis indeks baru melalui .push ()

var myarray = ["one","two","three"];

Memang paling baik ditangani via for(initialization;condition;update){

Tapi bagaimana dengan:

var myarray = [];
myarray[100] = "foo";
myarray.push("bar");

Coba ini:

var myarray = [], i;
myarray[100] = "foo";
myarray.push("bar");
myarray[150] = "baz";
myarray.push("qux");
alert(myarray.length);
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}

Mungkin bukan penggunaan array yang terbaik, tetapi hanya sebuah ilustrasi yang tidak selalu jelas.

Jika Anda tahu kunci Anda, dan pasti jika itu bukan bilangan bulat, satu-satunya pilihan struktur seperti array adalah objek.

var i, myarray= {
   "first":"john",
   "last":"doe",
   100:"foo",
   150:"baz"
};
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}
menyeberang
sumber
"Hanya gunakan ini pada objek" berarti jangan menggunakannya pada Array atau apa pun yang memperluas Object, jika tidak, seperti yang Anda tunjukkan, itu akan sangat konyol karena semuanya memanjang Object
Juan Mendes
'"array asosiatif", "kamus", "hash", "objek", semua konsep pemrograman ini berlaku untuk satu struktur di JS.' Tidak. Mereka adalah konsep yang berbeda dari bahasa yang berbeda, dengan kesamaan satu sama lain. Tetapi jika Anda berasumsi bahwa mereka / persis sama / dan untuk digunakan dengan cara yang sama, untuk tujuan yang sama, Anda mengatur diri sendiri untuk membuat beberapa kesalahan yang benar-benar bodoh yang bisa Anda hindari dengan menjadi kurang tahu tentang bagaimana bahasa Anda menggunakan karya.
Breton
2

Tentunya agak ekstrem untuk dikatakan

... tidak pernah menggunakan for in loop untuk menghitung lebih dari satu array. Tidak pernah. Gunakan good old for (var i = 0; i <arr.length; i ++)

?

Layak menyoroti bagian dalam ekstrak Douglas Crockford

... Bentuk kedua harus digunakan dengan benda ...

Jika Anda memerlukan array asosiatif (alias hashtable / kamus) di mana kunci dinamai bukannya diindeks secara numerik, Anda harus mengimplementasikan ini sebagai objek, misalnya var myAssocArray = {key1: "value1", key2: "value2"...};.

Dalam hal ini myAssocArray.lengthakan muncul nol (karena objek ini tidak memiliki properti 'panjang'), dan Anda i < myAssocArray.lengthtidak akan membuat Anda terlalu jauh. Selain memberikan kenyamanan yang lebih besar, saya berharap array asosiatif untuk menawarkan keunggulan kinerja dalam banyak situasi, karena kunci array dapat menjadi properti yang berguna (yaitu properti ID atau nama anggota array), yang berarti Anda tidak perlu beralih melalui yang panjang. array berulang kali mengevaluasi apakah pernyataan untuk menemukan entri array yang Anda cari.

Bagaimanapun, terima kasih juga untuk penjelasan dari pesan kesalahan JSLint, saya akan menggunakan cek 'isOwnProperty' sekarang ketika berinteraksi melalui berbagai array asosiatif saya!

Tomfumb
sumber
1
Anda sangat bingung. Tidak ada yang namanya "array asosiatif" dalam javascript. Itu hanyalah konsep php.
Breton
Memang benar bahwa benda-benda ini tidak memiliki lengthproperti, tetapi Anda dapat melakukannya dengan cara lain:var myArr = []; myArr['key1'] = 'hello'; myArr['key2'] = 'world';
nyuszika7h
3
@ Nyuszika7H Itu cara yang salah. Jika Anda tidak memerlukan bilangan bulat yang diindeks Array, Anda tidak boleh menggunakan var myArr = [], itu harus var myArr = {}dalam PHP mereka adalah hal yang sama, tetapi tidak di JS.
Juan Mendes
"Array asosiatif" bukan array.
Vincent McNabb
0

Hanya untuk menambahkan pada topik untuk di / untuk / $ masing-masing, saya menambahkan kasus uji jsperf untuk menggunakan $ .each vs untuk di: http://jsperf.com/each-vs-for-in/2

Browser / versi yang berbeda menanganinya secara berbeda, tetapi tampaknya $ .each dan langsung masuk adalah pilihan termurah untuk performa.

Jika Anda menggunakannya untuk beralih melalui array / objek asosiatif, mengetahui apa yang Anda cari dan mengabaikan semua yang lain, gunakan $ .each jika Anda menggunakan jQuery, atau hanya untuk di (dan kemudian istirahat; begitu Anda sudah mencapai apa yang Anda tahu harus menjadi elemen terakhir)

Jika Anda mengulangi melalui array untuk melakukan sesuatu dengan setiap pasangan kunci di dalamnya, harus menggunakan metode hasOwnProperty jika Anda TIDAK menggunakan jQuery, dan menggunakan $ .each jika Anda DO menggunakan jQuery.

Selalu gunakan for(i=0;i<o.length;i++)jika Anda tidak memerlukan array asosiatif ... lol chrome melakukan itu 97% lebih cepat dari pada untuk atau$.each

Benno
sumber