Soru Dizi yineleme ile neden “içeride…” kullanmak kötü bir fikirdir?


Kullanmamam söylendi for...in JavaScript dizileri ile. Neden olmasın?


1540
2018-02-01 09:46


Menşei


Birinin size söylediği son soruyu gördüm, ama sadece Arrays için. Diziler arasında yineleme yapmak, ancak mutlaka bir nesnenin üyeleri arasında yineleme yapmak için kötü bir uygulama olarak kabul edilir. - mmurch
"For" döngülerinin birçoğu için 'for (var i = 0; i <hColl.length; i ++) {}' ile karşılaştır 'var i = hColl.length; (i--) {} 'iken, ikinci formu kullanmanın mümkün olduğu zaman, büyük ölçüde daha hızlıdır. Bunun teğet olduğunu biliyorum ama bunu ekleyeceğimi düşündüm. - Mark Schultheiss
@MarkSchultheiss ama bu ters iterasyon. Daha hızlı olan iterasyonun başka bir sürümü var mı? - ma11hew28
@Wynand kullanımı var i = hCol1.length; for (i;i;i--;) {} Bir fark yaratacak şekilde i önbelleğe alın ve testi basitleştirin. - tarayıcı ne kadar eski olursa, aradaki fark daha fazladır for ve while HER ZAMAN "i" sayacını önbelleğe alır - ve tabii ki olumsuz durum her zaman duruma uymaz ve obfuscate  Bazı insanlar için kod biraz. ve not var i = 1000; for (i; i; i--) {} ve var b =1000 for (b; b--;) {} 1000 ile 1 arasında gittiğim ve b 999 ile 0 arasında gidip geliyor. - tarayıcı ne kadar eski olursa, o zaman performans için daha çok tercih ediliyor. - Mark Schultheiss
Ayrıca zeki olabilirsiniz. for(var i = 0, l = myArray.length; i < l; ++i) ... İleri iterasyon ile alabileceğiniz en hızlı ve en iyisidir. - Mathieu Amiot


Cevaplar:


Bunun sebebi şu:

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

bazen diğerlerinden tamamen farklı olabilir:

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

Ayrıca düşünün JavaScript kütüphaneler, sizin oluşturduğunuz herhangi bir diziyi etkileyecek gibi şeyler yapabilir:

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


1342
2018-02-01 10:08



Tarihsel olarak, bazı tarayıcılar 'uzunluk', 'toString' vb. - bobince
Kullanmayı unutma (var x in a) ziyade (x in a) - Küresel bir şey yaratmak istemiyorum. - Chris Morgan
İlk sayı, kötü bir sebep değil, yalnızca anlambilimindeki bir farklılık. İkinci sorun bana, yerleşik bir datatipin prototipini değiştiren bir nedenin (kütüphaneler arasındaki çatışmanın tepesinde) bir sebepten ziyade, kötüye değil, kötüye gidiyor gibi görünüyor. - Stewart
@Stewart: Tüm nesneleri JS, çağrıştırıcıdır. Bir JS Array bir nesnedir, bu yüzden evet, aynı zamanda ilişkilendiricidir, ancak bunun için değil. Bir nesnenin üzerinde yinelemek istiyorsanız anahtarlar, kullan for (var key in object). Bir dizinin üzerinde yinelemek istiyorsanız elementlerAncak, kullanımı for(var i = 0; i < array.length; i += 1). - Martijn
İlk örnek için söyledin, o Herkesin beklediği gibi 0'dan 4'e kadar sayısal indeksler üzerinde yinelemeler, BEN 0'dan 5'e kadar yinelemesini bekler! Konum 5'e bir eleman eklerseniz, dizi 6 öğeye (5 tanesinin tanımlanmamış) sahip olacaktır. - stivlo


for-in kendi başına bir beyan "kötü bir uygulama" değildir, ancak olabilir yanlış kullanılanörneğin yinelemek diziler veya dizi benzeri nesneler üzerinde.

Amacının for-in deyim saymak nesne özellikleri. Bu ifade aynı zamanda prototip zincirinde de yukarı doğru çıkacak. miras özellikleri, bir şey ara sıra arzu edilmez.

Ayrıca, yineleme sırası, bir dizi nesnesini "yinelemek" istiyorsanız, bu ifadeyle, özelliklerin (dizi dizinlerinin) sayısal düzende ziyaret edileceğinden emin olamayacağınız anlamına gelir.

Örneğin, JScript'te (IE <= 8), Array nesnelerinde bile numaralandırma sırası özellikler oluşturulduğunda tanımlanır:

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
}

Ayrıca, miras alınan mülkler hakkında konuşmak, örneğin, Array.prototype nesne (MooTools'un yaptığı gibi bazı kütüphaneler gibi), bu özelliklerin ayrıca numaralandırılacağı:

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

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

Daha önce de söylediğim gibi yinelemek diziler veya dizi benzeri nesneler üzerinde, en iyi şey bir sıralı döngüsade bir eski gibi for/while döngü.

Sadece numaralandırmak istediğinizde kendi mülkleri bir nesnenin (miras almayanlar), hasOwnProperty yöntem:

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

Ve bazı insanlar, doğrudan Object.prototype birisi adında bir özellik eklerse sorun yaşamamak hasOwnProperty bizim nesneye:

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

356
2017-11-23 21:22



Ayrıca bkz. David Humphrey'in yazısı JavaScript'te Nesneleri Yinelemek Hızlıca - dizi için for..in "normal" döngülerden çok daha yavaştır. - Chris Morgan
"HasOwnProperty" hakkında son nokta hakkında soru: Bir kullanıcı bir nesneyi "hasOwnProperty" geçersiz kılarsa, sorunlarınız olur. Birisi "Object.prototype.hasOwnProperty" geçersiz kılarsa, aynı sorunlara sahip değil misiniz? Her iki şekilde de seni mahvediyorlar ve bu senin sorumluluğunda değil mi? - Scott Rippey
Diyorsun for..in kötü bir uygulama değildir, ancak yanlış kullanılabilir. Kalıtımsal özellikler de dahil olmak üzere tüm nesne özelliklerinden gerçekten geçmek istediğiniz gerçek bir uygulama örneği var mı? - rjmunro
@ScottRippey: Eğer oraya almak istiyorsan: youtube.com/watch?v=FrFUI591WhI - Nathan Wall
Bu cevap ile değeri ile erişebileceğini buldum for (var p in array) { array[p]; } - Equiman


Kullanmamanız için üç neden var for..in dizi öğeleri üzerinde yinelemek için:

  • for..in olmayan dizi nesnesinin kendi tüm ve devralınan özellikleri üzerinde döngü olacak DontEnum; Bu, birisi belirli dizi nesnesine özellikler eklerse (bunun için geçerli nedenler vardır - bunu kendim yaptım) veya değiştiyse Array.prototype (diğer betiklerle iyi çalışması gereken kodda kötü uygulama olarak kabul edilir), bu özellikler de yinelenecektir; devralınan özellikler kontrol edilerek hariç tutulabilir hasOwnProperty()ancak bu, array nesnesinin kendisinde belirlenen özelliklerle size yardımcı olmaz

  • for..in eleman siparişini korumak garanti edilmez

  • yavaştır çünkü dizi nesnesinin tüm özelliklerini ve tüm prototip zincirini yürütmeniz gerekir ve yine de yalnızca mülkün adını alır, yani değeri almak için, ek bir arama gerekli olacaktır


101
2018-02-01 14:04





Çünkü ... diziyi barındıran nesneyi saymaz, dizinin kendisini değil. Diziler prototip zincirine bir işlev eklerseniz, bu da dahil edilecektir. Yani

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

Bu yazacak:

0 = foo
1 = çubuk
myOwnFunction = işlev () {uyarı (bu); }

Prototip zincirine hiçbir şeyin eklenmeyeceğinden asla emin olamayacağınız için diziyi numaralandırmak için bir for döngüsünü kullanın:

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

Bu yazacak:

0 = foo
1 = çubuk

48
2018-02-01 10:08



Diziler Hangi Nesneler, "diziyi tutan nesne" yoktur. - RobG


Ayrı olarak, dizilerde for-in kullanmada yanlış bir şey yoktur. Bir nesnenin özellik isimleri üzerinde yinelenen iteratlar ve bir "kullanıma hazır" dizisi durumunda, özellikler dizi dizinlerine karşılık gelir. (Yerleşik mülkler gibi length, toString ve benzerleri yineleme dahil değildir.)

Ancak, kodunuz (ya da kullandığınız çerçeve) dizilere ya da dizi prototipine özel özellikler eklerse, bu özellikler muhtemelen istediğiniz gibi olmayan yinelemeye dahil edilir.

Prototype gibi bazı JS çerçeveleri, Array prototipini değiştirir. JQuery gibi diğer çerçeveler, JQuery ile güvenli bir şekilde for-in kullanabilirsiniz.

Şüpheniz varsa, büyük ihtimalle for-in'i kullanmamalısınız.

Bir dizi boyunca yinelemenin alternatif bir yolu bir for döngüsü kullanmaktır:

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

Ancak, bunun farklı bir sorunu var. Sorun, bir JavaScript dizisinin "deliklere" sahip olmasıdır. Tanımlarsanız arr gibi:

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

Ardından dizinin iki öğesi vardır, ancak 101'lik bir uzunluğa sahiptir. For-in kullanılırken iki indeks elde edilirken for-loop 101 indeks verirken, 99 değeri undefined.


37
2018-02-01 10:34





Diğer cevaplarda verilen nedenlere ek olarak, sayaç için değişken ile matematik yapmak gerekiyorsa "for ... in" yapısını kullanmak istemeyebilirsiniz çünkü döngü, nesnenin özelliklerinin isimleri boyunca yinelenir ve bu nedenle değişken bir dizedir.

Örneğin,

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

yazacak

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

buna karşılık,

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

yazacak

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

Tabii ki, bu dahil ederek kolayca üstesinden gelebilir

ii = parseInt(ii);

döngüde, ancak ilk yapı daha doğrudan.


28
2017-09-02 02:29



Önek kullanabilirsiniz + yerine parseInt Gerçekten tamsayıya ihtiyacınız yoksa veya geçersiz karakterleri yoksayarsanız. - Konrad Borowski
Ayrıca, parseInt() tavsiye edilmez. Deneyin parseInt("025"); ve o irade başarısız. - Derek 朕會功夫
@Derek 朕 會 功夫 - Kesinlikle kullanabilirsiniz parseInt. Sorun, radix içermezseniz, eski tarayıcılar sayıyı yorumlamaya çalışabilir (böylece 025 sekizlik olur). Bu, ECMAScript 5'te düzeltildi, ancak "0x" ile başlayan sayılar için hala geçerli olur (sayıyı hex olarak yorumlar). Güvenli tarafta olmak için, sayıyı belirtmek üzere sayıyı kullanın. parseInt("025", 10) - bu, üs 10'u belirtir. - IAmTimCorey


2016 itibariyle (ES6) kullanabiliriz for…of John Slegers'ın zaten fark ettiği gibi, dizi yineleme için.

Bu basit tanıtım kodunu, işleri daha net hale getirmek için eklemek isterim:

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 gösterir:

for...of:

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

for...in:

0: 5
1: foo

Diğer bir deyişle:

  • for...of 0'dan 5'e kadar sayar ve aynı zamanda göz ardı eder Array.prototype.foo. Dizi gösterir değerler.

  • for...in sadece listeler 5undefined array indekslerini göz ardı etmek, ancak eklemek foo. Dizi gösterir özellik isimleri.


24
2018-03-10 04:29





Gerçeğinin yanı sıra for...in tüm sayısız özellikler üzerinde döngüler ( değil "tüm dizi elemanları" ile aynı!) http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdfbölüm 12.6.4 (5. baskı) veya 13.7.5.15 (7. baskı):

Mekaniği ve sipariş özelliklerin numaralandırılması ... belirtilmemiş...

(Vurgula benim.)

Bu, bir tarayıcı isterse, özelliklerin içinden geçtikleri sırayla geçebileceği anlamına gelir. Ya da sayısal sırayla. Ya da sözcüksel düzende ("30" dan "4" önce gelir!) Tüm nesne tuşlarını ve tüm dizi dizinlerini unutmayın - aslında dizgiler, bu yüzden tam anlam ifade eder). Karma tablolar olarak nesneleri uygularsa, kova yoluyla geçebilir. Veya bunlardan herhangi birini alıp "geriye" ekleyin. Bir tarayıcı bile yineleyebilir rasgele ve her mülkü tam olarak bir kez ziyaret ettiği sürece ECMA-262 uyumludur.

Pratikte, çoğu tarayıcı şu anda kabaca aynı sırada yinelemek istemektedir. Ama yapmaları gereken bir şey yok. Bu, uygulamaya özgüdür ve başka bir yolun daha verimli olduğu tespit edildiğinde, herhangi bir zamanda değişebilir.

Öyle ya da böyle, for...in onunla birlikte bir anlam çağrıştırmaz. Siparişi önemsiyorsanız, bu konuda açık olun ve düzenli kullanın for dizin ile döngü.


21
2018-05-14 16:26





Kısa cevap: Sadece buna değmez.


Daha uzun cevap: Sıralı eleman sırası ve optimum performans gerekmiyor olsa bile, buna değmez.


Uzun cevap: Aşağıdaki sebeplerden dolayı buna değmez:

  • kullanma for (var i in array) {} 'dizi'nin başka herhangi bir şekilde yorumlanmasına neden olacak saf Nesne özellik zincirini çaprazlayan ve sonuçta dizin tabanlı bir yöntemden daha yavaş gerçekleştiren nesne for döngü.
  • Nesne özelliklerinin beklendiği gibi sıralı olarak döndürülmesi garanti edilmez.
  • kullanma hasOwnProperty() veya isNaN() nesne özelliklerini filtrelemek için denetler ek bir ek yükü (daha fazla) daha yavaş gerçekleştirmesine neden olur. Ayrıca, böyle bir ek mantık getirilmesi, ilk etapta kullanılmasının temel sebebini, yani daha kısa bir formattan dolayı reddeder.

Bu nedenlerden dolayı performans ve kolaylık arasında kabul edilebilir bir takas mevcut değildir. Aslında, diziyi bir dizi olarak ele almak istemedikçe, hiçbir faydası yoktur. saf nesne nesnesinin özelliklerine nesne uygular ve gerçekleştirir.


20
2018-03-14 07:12