JSLint hatası 'for in gövdesi bir if deyimine sarılmalıdır' ne anlama geliyor?


242

Bir JavaScript dosyamda JSLint kullandım . Hatayı attı:

for( ind in evtListeners ) {

41. satır 9'daki sorun: Prototipten istenmeyen özellikleri filtrelemek için bir for in öğesinin bir if ifadesine sarılması gerekir.

Ne anlama geliyor?


5
Varsayılan olarak 'in' devralınan özellikleri de yineler. Genellikle, gövde if (evtListeners.hasOwnProperty(ind))işlemeyi yalnızca kendi (kalıtsal olmayan) özelliklerle sınırlamak için sarılır . Yine de, bazı durumlarda, miras alınanlar da dahil olmak üzere tüm mülkleri yinelemek istersiniz. Bu durumda, JSLint, gerçekten hangi özellikleri istediğinize karar vermek için döngü gövdesini bir if ifadesine sarmaya zorlar. Bu işe yarayacak ve JSlint'i mutlu edecek:if (evtListeners[ind] !== undefined)
xorcus

1
Çoğu cevap eski. güncellenmiş bir çözüm stackoverflow.com/a/10167931/3138375
eli-bd

Yanıtlar:


430

Her şeyden önce, bir dizi üzerinde numaralandırmak için asla bir for indöngü kullanmayın . Asla. Eskiyi kullan for(var i = 0; i<arr.length; i++).

Bunun sebebi şudur: JavaScript'teki her nesnenin adlı özel bir alanı vardır prototype. Bu alana eklediğiniz her şeye, o türdeki her nesnede erişilebilir olacak. Tüm dizilerin filter_0sıfırları filtreleyecek yeni ve harika bir fonksiyona sahip olmasını istediğinizi varsayalım .

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]

Bu, nesneleri genişletmenin ve yeni yöntemler eklemenin standart bir yoludur. Birçok kütüphane bunu yapıyor. Ancak, for inşimdi nasıl çalıştığına bakalım :

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

Görüyor musun? Aniden filter_0'ın başka bir dizi indeksi olduğunu düşünüyor. Tabii ki, bu gerçekten bir sayısal dizin for indeğil, sadece sayısal dizinler değil, nesne alanları üzerinden numaralandırılır. Şimdi her sayısal indeks üzerinden numaralandırıyoruz ve filter_0 . Ancak filter_0belirli bir dizi nesnesinin alanı değil, her dizi nesnesinin şimdi bu özelliği vardır.

Neyse ki, tüm nesnelerin hasOwnPropertybu alanın gerçekten nesnenin kendisine mi yoksa prototip zincirinden miras alındığını ve böylece bu türdeki tüm nesnelere ait olup olmadığını kontrol eden bir yöntemi vardır.

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

Not bu kod çalışır halde diziler için beklendiği gibi, bunu kullanmalısınız asla asla , kullanım for inve for each indiziler için. for inDizi dizinleri veya değerleri değil, bir nesnenin alanlarını numaralandırdığını unutmayın .

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

43
for inDiziler üzerinde yineleme yapmak için kullanmamalısınız , çünkü dil for inbir dizi üzerinde numaralandırılacak sıraya göre sıralanmaz. Sayısal sırada olmayabilir. Ayrıca, `for (i = 0; i <array.length; i ++) stil yapısını kullanırsanız, yalnızca sayısal dizinleri sırayla yinelediğinizden ve alfasayısal özellik olmadığından emin olabilirsiniz .
Breton

Teşekkürler! Bunu referans olarak kaydedeceğim.
nyuszika7h

Kendimi tekrar bu cevaba bakarken buldum çünkü JSLint'in bu parçasının kırıldığına ikna oldum. Kabaca olan bir kod vardı: for (dinleyiciler o) {if (listeners.hasOwnProperty (i)) {console.log (o); }} Sorun ben bir hata vardı, ben değişken isimleri i o değiştirdi ve bir referans kaçırdı. JSLint hasOwnProperty'yi doğru nesne üzerinde doğru özellik için denetlediğinizden emin olmak için yeterince akıllıdır.
19'da drewish

12
bununla birlikte, bir nesnenin özelliği üzerinde yineleme yapmak iyidir. OP hiçbir zaman dizisinin bir Diziye uygulandığını söylemedi. HasOwnProperty en iyi uygulamadır, ancak istemediğiniz durumlar vardır - örneğin bir nesne başka bir nesneyi genişletirse ve hem nesneleri hem de 'üst' kişinin özelliklerini listelemek istiyorsanız.
gotofritz

3
İnsanları for-indöngülerden korkutmak yerine (bu arada harika), onları nasıl çalıştıklarını (bu cevapta düzgün bir şekilde yapılıyor) eğitmeli Object.defineProperty()ve prototiplerini hiçbir şey kırmadan güvenli bir şekilde genişletebilmeleri için onları eğitmeliyiz . Bu arada, uzayan yerli nesnelerin prototipler gerektiğini değil olmadan yapılabilir Object.defineProperty.
Robert Rossmann

87

Jslint'in yazarı Douglas Crockford, bu konu hakkında birçok kez yazdı (ve konuştu). Web sitesinin bu sayfasında aşağıdakileri kapsayan bir bölüm var :

Açıklama için

İfade sınıfları için A aşağıdaki biçime sahip olmalıdır:

for (initialization; condition; update) {
    statements
}

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

İlk form, dizilerle ve önceden belirlenmiş sayıda yinelemenin döngülerinde kullanılmalıdır.

İkinci form nesnelerle birlikte kullanılmalıdır. Nesnenin prototipine eklenen üyelerin numaralandırmaya dahil edileceğini unutmayın. Nesnenin gerçek üyelerini ayırt etmek için hasOwnProperty yöntemini kullanarak defalarca programlamak akıllıca olacaktır:

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

Crockford'un YUI tiyatrosunda bunun hakkında konuştuğu bir video dizisi var. Crockford'un javascript hakkında videolar / konuşmalar dizisi, javascript hakkında biraz ciddi olup olmadığınızı görmek için bir zorunluluktur.


21

Kötü: (jsHint hata verir)

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

İyi:

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

8

Vava'nın cevabı işaretin üstünde. JQuery kullanıyorsanız, $.each()işlev bununla ilgilenir, bu nedenle kullanımı daha güvenlidir.

$.each(evtListeners, function(index, elem) {
    // your code
});

5
Performans burada herhangi bir düşünce ise, ham döngü ile kurtulabilirsiniz $.each(veya underscore.js _.each) kullanmanızı tavsiye etmem for. jsperf, çalıştırılmaya değer birkaç göz açıcı karşılaştırma testine sahiptir.
nickb

3
Bu ( jsperf.com/each-vs-each-vs-for-in/3 ) daha temeldir çünkü temel proto filtreyi kullanır
dvdrtrgn

7

@all - JavaScript'teki her şey bir nesnedir (), bu nedenle "bunu yalnızca nesnelerde kullanın" gibi ifadeler biraz yanıltıcıdır. Ayrıca, JavaScript = 1 == "1" doğru olacak şekilde güçlü bir şekilde yazılmamıştır (1 === "1" olmamasına rağmen, Crockford bu konuda büyüktür). JS'deki progromatik diziler söz konusu olduğunda, tanımda yazım önemlidir.

@Brenton - Bir terminoloji diktatörü olmaya gerek yok; "ilişkilendirilebilir dizi", "sözlük", "karma", "nesne", bu programlama kavramlarının tümü JS'deki tek bir yapı için geçerlidir. Ad (anahtar, dizin) değer çiftleridir, burada değer başka bir nesne olabilir (dizeler de nesnedir)

Yani, new Array()aynı[]

new Object() kabaca benzer {}

var myarray = [];

Tüm dizinlerin (aka anahtarlar) tam sayı olması gereken kısıtlamaya sahip bir dizi olan bir yapı oluşturur. Ayrıca .push () aracılığıyla yeni dizinlerin otomatik olarak atanmasına izin verir.

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

Gerçekten en iyi şekilde ele alınır for(initialization;condition;update){

Ama ne hakkında:

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

Bunu dene:

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]);
    }
}

Belki bir dizinin en iyi kullanımı değil, sadece şeylerin her zaman net olmadığı bir örnek.

Anahtarlarınızı biliyorsanız ve kesinlikle tam sayı değilse, yapı gibi tek diziniz nesnedir.

var i, myarray= {
   "first":"john",
   "last":"doe",
   100:"foo",
   150:"baz"
};
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}

"Bunu yalnızca nesnelerde kullanın", onu Dizilerde veya Nesneyi genişleten başka bir şeyde kullanmamanız anlamına gelir, aksi takdirde, işaret ettiğiniz gibi, her şey Nesne'yi genişlettiği için çok aptalca olur
Juan Mendes

'"ilişkisel dizi", "sözlük", "karma", "nesne", bu programlama kavramlarının tümü JS'deki bir yapı için geçerlidir.' Hayır. Farklı dillerden, birbirleriyle benzerlik gösteren farklı kavramlardır . Ancak, aynı olduklarını / aynı şekilde kullanıldıklarını varsayarsanız ve aynı amaçlar için, dilin nasıl olduğu konusunda daha az cahil davranarak kaçınabileceğiniz gerçekten aptalca hatalar yapmak için kendinizi hazırlıyorsunuz. eserleri kullanıyorsunuz.
Breton

2

Elbette söylemesi biraz aşırı

... bir dizi üzerinde numaralandırmak için hiçbir zaman for in döngüsü kullanmayın. Asla. Şunun için iyi eskiyi kullanın (var i = 0; i <arr.length; i ++)

?

Douglas Crockford ekstresindeki bölümü vurgulamaya değer

... İkinci form nesnelerle kullanılmalı ...

Anahtarlar yerine sayısal endeksli ait adlandırılır ilişkilendirilebilir bir dizi (aka hashtable / sözlük) gerekiyorsa, örneğin bir nesne olarak bu uygulamaya sahip olacaktır var myAssocArray = {key1: "value1", key2: "value2"...};.

Bu durumda myAssocArray.lengthnull ortaya çıkar (çünkü bu nesnenin 'length' özelliği yoktur) ve i < myAssocArray.lengthsizi çok ileri götürmezsiniz. Daha fazla kolaylık sağlamanın yanı sıra, dizi anahtarları yararlı özellikler (yani bir dizi üyesinin ID özelliği veya adı) olabileceğinden, ilişkilendirilebilir dizilerin birçok durumda performans avantajları sunmasını beklerim, yani uzun bir yineleme yapmak zorunda değilsiniz dizi, peşinde olduğunuz dizi girişini bulmak için if ifadelerini tekrar tekrar değerlendirir.

Her neyse, JSLint hata mesajlarının açıklaması için de teşekkürler, sayısız ilişkilendirilebilir dizilerle entegrasyon yaparken şimdi 'isOwnProperty' kontrolünü kullanacağım!


1
Çok kafasınız. Javascript'te "çağrışımsal diziler" diye bir şey yoktur. Bu kesinlikle bir php konseptidir.
Breton

Bu nesnelerin bir lengthözelliği olmadığı doğrudur , ancak başka bir şekilde yapabilirsiniz:var myArr = []; myArr['key1'] = 'hello'; myArr['key2'] = 'world';
nyuszika7h

3
@ Nyuszika7H Bu yanlış yol. Dizin tamsayı dizine ihtiyacınız yoksa, kullanmamalısınız var myArr = [], var myArr = {}PHP'de aynı şey olmalı , ancak JS'de olmamalıdır.
Juan Mendes

İlişkilendirilebilir "dizi" bir dizi değil.
Vincent McNabb


0

Sadece in / for / $ için konuya eklemek için, her biri $ .each vs for kullanmak için bir jsperf test örneği ekledim: http://jsperf.com/each-vs-for-in/2

Farklı tarayıcılar / sürümler farklı şekilde işlemektedir, ancak $ .each ve in için olduğu gibi performans açısından en ucuz seçenekler.

İlişkilendirilebilir bir dizi / nesne üzerinden yineleme yapmak, sonra ne yaptığınızı bilmek ve diğer her şeyi göz ardı etmek için kullanıyorsanız, jQuery kullanıyorsanız $ .each veya yalnızca in için (ve sonra bir mola; bir kez son öğe olması gerektiğini bildiğiniz şeye ulaştı)

İçindeki her anahtar çifti ile bir şeyler gerçekleştirmek için bir dizi üzerinden yineliyorsanız, jQuery kullanmadığınız takdirde hasOwnProperty yöntemini kullanmanız ve jQuery kullanıyorsanız DO $ .each kullanmanız gerekir.

İlişkisel for(i=0;i<o.length;i++)bir diziye ihtiyacınız yoksa her zaman kullanın ... lol chrome, bir in veya$.each

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.