Dizi yinelemesinde “for… in” kullanmak neden kötü bir fikir?


1823

for...inJavaScript dizileriyle birlikte kullanmamam söylendi . Neden olmasın?


45
Birinin size söylediği son soruyu gördüm, ama sadece Diziler içindi. Diziler aracılığıyla yineleme yapmak için kötü bir uygulama olarak kabul edilir, ancak bir nesnenin üyeleri aracılığıyla yineleme için zorunlu değildir.
mmurch

19
'Var' ile 'for (var i = 0; i <hColl.length; i ++) {}' ile "for" döngülerine sahip birçok yanıt 'var i = hColl.length; (i--) {} ', ikinci formun kullanılması mümkün olduğunda oldukça hızlıdır. Bunun teğet olduğunu biliyorum ama bu parçayı ekleyeceğimi düşündüm.
Mark Schultheiss

2
@MarkSchultheiss ama bu tersine yineleme. İleri yinelemenin daha hızlı olan başka bir sürümü var mı?
ma11hew28

5
@ var i = hCol1.length; for (i;i;i--;) {}İ ve fark yaratacak şekilde i önbelleğini kullanın ve testi basitleştirin. - tarayıcı eskidikçe, ALWAYS arasındaki daha fazla fark forve whileHER ZAMAN "i" sayacını önbelleğe alır - ve elbette negatif her zaman duruma uymaz ve obfuscate kod bazı insanlar için biraz da olsa olumsuz olur . ve not var i = 1000; for (i; i; i--) {}ve var b =1000 for (b; b--;) {}ben 1000-1 1 ve b 999-0 0 gider. - tarayıcı büyüdükçe, performans için daha fazla tercih eğilimindedir.
Mark Schultheiss

9
Ayrıca akıllı da olabilirsiniz. for(var i = 0, l = myArray.length; i < l; ++i) ...ileri yineleme ile elde edebileceğiniz en hızlı ve en iyisidir.
Mathieu Amiot

Yanıtlar:


1557

Bunun nedeni, bir yapının:

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ğerinden 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, JavaScript kitaplıklarının oluşturduğunuz dizileri etkileyecek şekilde böyle şeyler yapabileceğini düşünün :

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


146
Tarihsel olarak, bazı tarayıcılar 'uzunluk', 'toString' vb.
bobince

398
Kullanmayı unutmayın (var x in a)ziyade (x in a)- küresel oluşturmak istemiyoruz.
Chris Morgan

78
İlk sorun bunun kötü bir nedeni değil, sadece anlambilimdeki bir farktır. İkinci sorun bana, (aynı şeyi yapan kütüphaneler arasındaki çatışmaların üstünde), yerleşik bir veri tipinin prototipini değiştirmenin bunun için değil, kötü olduğu için bir neden gibi görünüyor.
Stewart

86
@Stewart: JS'deki tüm nesneler ilişkilendirilebilir. Bir JS Dizisi bir nesnedir, bu yüzden evet, ilişkilendirilebilir, ancak bunun için değildir. Bir nesnenin anahtarları üzerinde yineleme yapmak istiyorsanız, tuşunu kullanın for (var key in object). Bununla birlikte, bir dizinin öğeleri üzerinden yineleme yapmak istiyorsanız , kullanın for(var i = 0; i < array.length; i += 1).
Martijn

42
İlk örnek için, 0'dan 4'e kadar sayısal dizinler üzerinde yinelendiğini söylediniz , herkesin beklediği gibi , 0'dan 5'e kadar yinelenmesini bekliyorum ! Eğer konum 5'e bir eleman eklerseniz, dizi 6 eleman içerecektir (bunlardan 5 tanesi tanımsızdır).
stivlo

393

for-inKendisi tarafından ifadesi ancak olabilir, bir "kötü uygulama" değil yanlış kullanılan , örneğin, için yineleme diziler veya nesnelerin dizi benzeri bitti.

Amacı for-inifadesine olduğunu numaralandırmak nesne özelliklerini üzerinde. Bu ifade, bazen istenen bir şey olmayan kalıtsal özellikler üzerinde numaralandıran prototip zincirinde yukarı çıkacaktır .

Ayrıca, yineleme sırası spesifikasyon tarafından garanti edilmez, yani bir dizi nesnesini "yinelemek" istiyorsanız, bu ifade ile özelliklerin (dizi indeksleri) sayısal sırayla ziyaret edileceğinden emin olamazsınız.

Örneğin, JScript'te (IE <= 8), Array nesnelerinde bile numaralandırma sırası özellikler oluşturuldukça 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, örneğin Array.prototypenesneyi genişletirseniz (MooTools'un yaptığı gibi bazı kütüphaneler gibi) devralınan özellikler hakkında konuşursanız , bu özellikler de numaralandırılır:

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

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

Daha önce söylediğim gibi, diziler veya dizi benzeri nesneler üzerinde yineleme yapmak için en iyi şey, düz-eski / döngü gibi sıralı bir döngü kullanmaktır .forwhile

Bir nesnenin yalnızca miras alınmamış özelliklerini (miras alınmamış olanlar) numaralandırmak istediğinizde , hasOwnPropertyyöntemi kullanabilirsiniz :

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

Ve hatta bazıları, Object.prototypenesnemize bir özellik ekliyorsa sorun yaşamamak için yöntemi doğrudan çağırmayı öneriyorlar hasOwnProperty:

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

10
Ayrıca bkz. David Humphrey'in JavaScript'teki Nesneler Üzerinde Yinelemesini Hızla Gönderme - dizi için for..in"normal" döngülerden çok daha yavaş.
Chris Morgan

17
"HasOwnProperty" ile ilgili son nokta hakkında soru: Birisi bir nesnede "hasOwnProperty" yi geçersiz kılarsa, sorun yaşayacaksınız. Ancak, birisi "Object.prototype.hasOwnProperty" yi geçersiz kılarsa aynı sorunlarınız olmaz mı? Her iki durumda da seni mahvediyorlar ve bu senin sorumluluğun değil mi?
Scott Rippey

for..inKötü uygulama olmadığını söylüyorsunuz , ancak yanlış kullanılabilir. Miras alınan mülkler de dahil olmak üzere tüm nesne özelliklerinden geçmek istediğiniz gerçekten iyi uygulamalara dair gerçek bir dünya örneğiniz var mı?
rjmunro

4
@ScottRippey: Oraya götürmek istiyorsanız: youtube.com/watch?v=FrFUI591WhI
Nathan Wall

bu cevapla değeri ile erişebilirsiniz bulundufor (var p in array) { array[p]; }
equiman

117

for..inDizi öğeleri üzerinde yineleme yapmak için kullanmamanızın üç nedeni vardır :

  • for..indizi nesnesinin kendi ve devralınan tüm özellikleri üzerinde döngü yapar DontEnum; Birisi belirli bir dizi nesnesine özellikler eklerse (bunun için geçerli nedenler var - kendim yaptım) veya değiştiğinde Array.prototype(diğer komut dosyalarıyla iyi çalışması beklenen kodda kötü uygulama olarak kabul edilir), bu özellikler aynı zamanda tekrarlanmalı; devralınan özellikler kontrol edilerek hariç tutulabilir hasOwnProperty(), ancak bu dizi nesnesinin kendisinde ayarlanan özelliklerle size yardımcı olmaz

  • for..in eleman siparişini koruyacağı garanti edilmez

  • yavaştır, çünkü dizi nesnesinin tüm özelliklerini ve tüm prototip zincirini yürümek zorunda kalırsınız ve yine de sadece mülkün adını alırsınız, yani değeri almak için ek bir arama gerekecektir


55

Çünkü ... için, dizinin kendisini değil, diziyi tutan nesne boyunca numaralandırılır. Diziler prototip zincirine bir işlev eklersem, buna da dahil edilir. 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 yazacaktır:

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

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

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

Bu yazacaktır:

0 = foo
1 = çubuk

16
Diziler olan nesneler, hayır "dizisini tutan nesne" var.
RobG

41

İzolasyonda, dizilerde for-in kullanmanın yanlış bir yanı yoktur. For-in, bir nesnenin özellik adları üzerinden yinelenir ve "kutudan çıktığı gibi" bir dizi durumunda, özellikler dizi dizinlerine karşılık gelir. (Gibi Özelliklere yerleşik length, toStringböylece yineleme dahil edilmez ve.)

Ancak, kodunuz (veya kullandığınız çerçeve) dizilere veya 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 bunu yapmaz, bu nedenle JQuery ile güvenli bir şekilde for-in kullanabilirsiniz.

Şüpheniz varsa, muhtemelen for-in kullanmamalısınız.

Bir dizi üzerinden yinelemenin alternatif bir yolu for-loop kullanmaktır:

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

Ancak bunun farklı bir sorunu var. Sorun, bir JavaScript dizisinin "delikleri" olabilmesidir. Eğer arrolarak tanımlarsanız :

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

Daha sonra dizinin iki öğesi vardır, ancak uzunluğu 101'dir. For-in kullanımı iki dizin verirken, for-döngüsü 101 indeksi verir, burada 99 değeri vardır undefined.


37

2016'dan (ES6) for…ofJohn Slegers'ın fark ettiği gibi dizi yinelemesi için kullanabiliriz .

Sadece işleri daha net hale getirmek için bu basit gösteri kodunu eklemek istiyorum:

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

for...of:

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

for...in:

0: 5
1: foo

Başka bir deyişle:

  • for...of0'dan 5'e kadar sayar ve ayrıca yok sayar Array.prototype.foo. Dizi değerlerini gösterir .

  • for...inyalnızca 5tanımsız dizi dizinlerini yok sayarak, ancak ekler foo. Dizi özellik adlarını gösterir .


32

Kısa cevap: Buna değmez.


Daha uzun cevap: Sıralı eleman sırası ve optimum performans gerekli olmasa bile buna değmez.


Uzun cevap: Buna değmez ...

  • Kullanılması , nesne prototip zincirini geçerek ve sonuçta dizin tabanlı bir döngüden daha yavaş performans göstererek bir nesne olarak yinelenmeye for (var property in array)neden olur .arrayfor
  • for (... in ...) beklendiği gibi nesne özelliklerini sıralı olarak döndürmesi garanti edilmez.
  • Nesne özelliklerini filtrelemek için hasOwnProperty()ve !isNaN()denetimlerini kullanmak , ek bir ek yüktür ve daha yavaş çalışmasına neden olur ve ilk etapta, yani daha özlü format nedeniyle kullanmanın temel nedenini ortadan kaldırır.

Bu nedenlerle performans ve rahatlık arasında kabul edilebilir bir denge bile yoktur. Amaç diziyi bir nesne olarak işlemek ve dizinin nesne özellikleri üzerinde işlemler yapmak olmadıkça gerçekten faydası yoktur .


31

Diğer cevaplarda verilen nedenlere ek olarak, sayaç değişkeni ile matematik yapmanız gerekiyorsa "for ... in" yapısını kullanmak istemeyebilirsiniz, çünkü döngü nesnenin özelliklerinin adları ve dolayısıyla değişken aracılığıyla yinelenir 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 edilerek kolayca aşılabilir

ii = parseInt(ii);

ancak ilk yapı daha doğrudan.


6
Sen öneki kullanabilirsiniz +yerine parseIntgerçekten tamsayı ihtiyaç veya görmezden sürece geçersiz karakter.
Konrad Borowski

Ayrıca, kullanılması parseInt()önerilmez. Deneyin parseInt("025");ve başarısız olacaktır .
Derek 朕 會 功夫

6
@Derek 朕 會 功夫 - kesinlikle kullanabilirsiniz parseInt. Sorun, sayı tabanı eklemezseniz, eski tarayıcılar sayıyı yorumlamaya çalışabilir (bu nedenle 025 sekizlik olur). Bu ECMAScript 5'te düzeltildi, ancak yine de "0x" ile başlayan sayılar için oluyor (sayıyı onaltılı olarak yorumlar). Şöyle sayısını belirtmek için tabanını kullanır, güvenli tarafta olmak için parseInt("025", 10)belirtir 10. dayandırmak olduğunu -
IAmTimCorey

23

Kenara olmasından for... intüm enumerable özellikleri üzerinde döngüler (ki değil "tüm dizi elemanları" ile aynı!), Bkz http://www.ecma-international.org/publications/files/ECMA-ST/Ecma -262.pdf , bölüm 12.6.4 (5. baskı) veya 13.7.5.15 (7. baskı):

Özelliklerin numaralandırılması mekaniği ve sırası belirtilmemiş ...

(Vurgu benim.)

Bu, bir tarayıcı isterse özelliklerin yerleştirildikleri sırayla geçebileceği anlamına gelir. Veya sayısal sırayla. Veya sözcüksel sıraya göre ("30", "4" ten önce gelir!) Tüm nesne anahtarlarını - ve böylece tüm dizi dizinlerini - aslında dizelerdir, bu yüzden tam anlamıyla mantıklıdır). Nesneleri karma tablolar olarak uygularsa, kovadan geçebilir. Veya bunlardan herhangi birini alın ve "geriye" ekleyin. Bir tarayıcı, rastgele bir şekilde yinelenebilir ve her mülkü tam olarak bir kez ziyaret ettiği sürece ECMA-262 uyumlu olabilir.

Uygulamada, çoğu tarayıcı şu anda kabaca aynı sırayla yinelemeyi sever. Ama yapmak zorunda olduklarını söyleyen hiçbir şey yok. Bu uygulamaya özgüdür ve başka bir yolun çok daha verimli olduğu tespit edilirse herhangi bir zamanda değişebilir.

Her iki durumda da, for... inberaberinde düzen çağrışımı yapmaz. Siparişe önem veriyorsanız, siparişinizi açıkça belirtin ve fordizin içeren normal bir döngü kullanın .


18

Temelde iki neden:

Bir

Diğerlerinin söylediği gibi, dizinizde olmayan veya prototipten devralınan anahtarlar alabilirsiniz. Diyelim ki, bir kütüphane Array veya Object prototiplerine bir özellik eklerse:

Array.prototype.someProperty = true

Bunu her dizinin bir parçası olarak alacaksınız:

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

bunu hasOwnProperty yöntemiyle çözebilirsiniz:

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

ancak bu, for-in döngüsüne sahip herhangi bir nesne üzerinde yineleme yapmak için geçerlidir.

İki

Genellikle bir dizideki öğelerin sırası önemlidir, ancak for-in döngüsü mutlaka doğru sırayla yinelenmez, çünkü diziyi bir nesne olarak ele alır, bu da JS'de uygulanma biçimidir, bir dizi olarak. Bu küçük bir şey gibi görünüyor, ancak uygulamaları gerçekten berbat edebilir ve hata ayıklamak zordur.


2
Object.keys(a).forEach( function(item) { console.log(item) } )prototipten miras alınanlar değil, kendi özellik anahtarları dizisini yineleyin.
Qwerty

2
Doğru, ancak for-in döngüsü gibi, mutlaka doğru dizin düzeninde olmayacaktır. Ayrıca, ES5'i desteklemeyen eski tarayıcılarda çalışmaz.
Lior

Bu tarayıcılara array.forEachkomut dosyalarınıza belirli bir kod ekleyerek öğretebilirsiniz . Bkz. Polyfill developer.mozilla.org/tr-TR/docs/Web/JavaScript/Reference/…
Qwerty

Tabii ki, ama sonra prototipi manipüle ediyorsunuz ve bu her zaman iyi bir fikir değil ... ve yine de sipariş meselesi var ...
Lior

Ve elbette, üç numaralı sebep: Seyrek diziler.
daha iyi bir oliver

16

Çünkü indeksler değil, nesne alanları üzerinden numaralandırılır. İndeks "uzunluk" ile değer alabilirsiniz ve bunu istediğinizden şüpheliyim.


Peki bunu yapmanın en iyi yolu nedir?
lYriCAlsSH

3
for (var i = 0; i <arr.length; i ++) {}
vava

3
Firefox 3'te arr.forEach veya for (var [i, v] 'de Iterator (arr)) {}' yi de kullanabilirsiniz, ancak forEach yöntemini kendiniz yazabilmenize rağmen, IE'de çalışmaz.
vava

ve neredeyse her kütüphanenin bunun için kendi yöntemi vardır.
vava

5
Bu cevap yanlış. "uzunluk", for-in yinelemeye dahil edilmeyecektir. Sadece kendinizi eklediğiniz özelliklerdir.
JacquesB

16

Ekleyecek çok şeyim olduğunu sanmıyorum. Triptych'in cevabı veya CMS'ninfor...in bazı durumlarda neden kullanılmasından kaçınması gerektiği cevabı .

Bununla birlikte, modern tarayıcılarda , for...inkullanılamayan durumlarda kullanılabilecek bir alternatif olduğunu eklemek isterim for...in. Bu alternatif for...of:

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

Not :

Ne yazık ki, Internet Explorer'ın hiçbir sürümü desteklenmiyor for...of( Edge 12+ destekliyor ), bu nedenle istemci tarafı üretim kodunuzda kullanabilmeniz için biraz daha beklemeniz gerekecek. Ancak, sunucu tarafı JS kodunuzda kullanmanız güvenli olmalıdır ( Node.js kullanıyorsanız ).


@georgeawg Demek istedin for-of, değil for-inmi?
ᆼ ᆺ ᆼ

15

İle ilgili sorun for ... in ...- ve bu sadece bir programcı dili gerçekten anlamadığında sorun haline gelir; bu gerçekten bir hata ya da herhangi bir şey değildir - bir nesnenin tüm üyeleri üzerinde yinelemesidir (iyi, tüm numaralandırılabilir üyeler, ancak şimdilik bir detay). Eğer üzerinde yineleme yapmak istediğinizde sadece bir dizi, tutarlı anlamsal şeyleri tutmak için tek garantili yolu endeksli özelliklerini (a olduğunu bir tamsayı indeksi kullanmaktır for (var i = 0; i < array.length; ++i)tarzı döngü).

Herhangi bir nesnenin kendisiyle ilişkili keyfi özellikleri olabilir. Özellikle bir dizi örneğine ek özellikler yükleme konusunda korkunç bir şey olmazdı. Bu nedenle , yalnızca dizinlenmiş dizi benzeri özellikleri görmek isteyen kodun bir tamsayı dizinine bağlı kalması gerekir . Tüm özellikleri görmek for ... inve gerçekten neye ihtiyaç duyduğunun tamamen farkında olan kod, o zaman bu da tamam.


Güzel açıklama Sivri. Sadece merak. Ben çarpma özellikleri altında bir nesnenin içinde bir dizi vardı ve for indöngü için düzenli olarak karşılaştırıldığında, bu diziler yinelenen alacaktı? (özünde, yavaş performans, değil mi?)
NiCk Newman

2
@NiCkNewman inbir for ... indöngüde sonra başvurduğunuz nesne iyi olacak
Pointy

Anlıyorum. Sadece merak ediyorum çünkü ana oyun nesnemin içinde nesneler ve diziler var ve vuruş için bunun endekslerde bir döngü için normalden daha acı verici olup olmadığını merak ediyordum.
NiCk Newman

@NiCkNewman tüm bu sorunun teması sadece for ... indizilerde kullanmamanızdır ; yapmamak için birçok iyi neden var. Bu bir performans sorunu değil, "kırılmadığından emin olun" sorunu.
Sivri

Nesnelerim teknik olarak bir dizide saklanıyor, bu yüzden endişelendim, şöyle bir şey: [{a:'hey',b:'hi'},{a:'hey',b:'hi'}]ama evet, anlıyorum.
NiCk Newman

9

Ayrıca, anlambilim nedeniyle, for, indizileri işleme biçimi (diğer bir JavaScript nesnesiyle aynıdır) diğer popüler dillerle hizalanmaz.

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

9

TL&DR:for in Döngüyü dizilerde kullanmak kötü değil, aslında tam tersi.

for inDöngü doğru dizilerde kullanılırsa JS bir mücevher olduğunu düşünüyorum . Yazılımınız üzerinde tam kontrole sahip olmanız ve ne yaptığınızı bilmeniz beklenir. Söz konusu dezavantajları görelim ve bunları tek tek çürütelim.

  1. Devralınan özellikler arasında da dolaşır: Her şeyden Array.prototypeönce yapılması gereken uzantılar kullanılarak yapılmalı Object.defineProperty()ve enumerabletanımlayıcıları olarak ayarlanmalıdır false. Bunu yapmayan hiçbir kütüphane hiç kullanılmamalıdır.
  2. Daha sonra devralma zincirine eklediğiniz özellikler sayılır: Dizi Object.setPrototypeOfveya Class'a göre dizi alt sınıflaması yaparken extend. Yine kullanması gereken Object.defineProperty()hangi varsayılan kümeleri tarafından writable, enumerableve configurablemülkiyet betimleyicilerinin false. Burada bir dizi alt sınıflandırma örneğini görelim ...

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

Gördüğünüz gibi .. for inDöngü kodunuzu önemsediğiniz için artık güvende.

  1. for inDöngü yavaş: Cehennem hayır. Zaman zaman gerekli olan seyrek diziler üzerinde döngü yapıyorsanız, en hızlı yineleme yöntemidir. Bu, kişinin bilmesi gereken en önemli performans numaralarından biridir. Bir örnek görelim. Seyrek bir dizi üzerinden döneceğiz.

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:");


@Ravi Shanker Reddy İyi bir kıyaslama oluşturuldu. for inCevabımda belirttiğim gibi, döngü seyrek ve daha büyük boyut alırsa daha diğerleri döngü "if" gölgeler. Bu yüzden rastgele endekslerde arrrastgele seçilen 50 ürün ile ~ 10000 boyutunda, seyrek bir dizi için tezgah testini yeniden düzenledim [42,"test",{t:1},null, void 0]. Farkı hemen fark edeceksiniz. - >> Buradan kontrol edin << - .
Redu

8

Diğer sorunlara ek olarak, "for..in" sözdizimi muhtemelen daha yavaştır, çünkü dizin bir tamsayı değil, bir dizedir.

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'

Muhtemelen çok önemli değil. Dizi öğeleri Dizi tabanlı veya Dizi benzeri bir nesnenin özellikleridir ve tüm nesne özelliklerinde dize anahtarları bulunur. JS motorunuz bir şekilde onu optimize etmezse, bir sayı kullansanız bile arama için bir dizeye dönüşür.
cHao

Herhangi bir performans sorununa bakılmaksızın, JavaScript'te yeniyseniz, var i in adizinin bir tamsayı olmasını ve beklemesini beklerseniz, bunun gibi bir şey yapmak a[i+offset] = <value>değerleri tamamen yanlış yerlere yerleştirir. ("1" + 1 == "11").
szmoore

8

Önemli bir özellik, for...inyalnızca numaralandırılabilir özellik özniteliği true olarak ayarlanmış bir nesnede bulunan özellikleri yinelemesidir . Bu nedenle, bir nesne kullanılarak bir nesne üzerinde yinelenmeye for...inçalışırsa, numaralandırılabilir özellik özniteliği yanlışsa, rastgele özellikler gözden kaçabilir. Normal Dizi nesneleri için numaralandırılabilir özellik özniteliğini değiştirmek, böylece belirli öğelerin numaralandırılmaması oldukça mümkündür. Genel olarak özellik özellikleri, bir nesnedeki işlev özelliklerine uygulanır.

Bir mülkün numaralandırılabilir özellik özelliğinin değerini şu şekilde kontrol edebilirsiniz:

myobject.propertyIsEnumerable('myproperty')

Veya dört özellik özniteliğinin tümünü elde etmek için:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

Bu, ECMAScript 5'te bulunan bir özelliktir - önceki sürümlerde numaralandırılabilir özellik özelliğinin değerini değiştirmek mümkün değildi (her zaman true olarak ayarlanmıştı).


8

for/ inKarışık tablolar (birleştirici dizileri), dizi (birleştirici olmayan): iki değişkenli türleri ile çalışır.

JavaScript, öğelerin içinden geçme şeklini otomatik olarak belirler. Dizinizin gerçekten ilişkisel olmadığını biliyorsanız for (var i=0; i<=arrayLen; i++), otomatik algılama yinelemesini atlayabilirsiniz.

Ama bence, bu kullanımda daha iyidir for/ ino Otomatik olarak algılanması için gerekli olan süreç çok azdır.

Bunun gerçek bir cevabı, tarayıcının JavaScript kodunu nasıl ayrıştırdığına / yorumladığına bağlı olacaktır. Tarayıcılar arasında değişebilir.

Kullanmamanın başka amaçlarını düşünemiyorum 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]);

prototip nesneleri kullanmıyorsanız true. ;) aşağıdaki
Ricardo

Thats çünkü Arrayolduğunu Objectçok
Ücretsiz Danışmanlık

2
for ... innesnelerle çalışır. Otomatik algılama diye bir şey yoktur.
daha iyi bir oliver

7

Çünkü dikkatli olmazsanız prototip zincirindeki nesnelere ait özellikleri yineleyecektir.

Bunu kullanabilirsiniz for.. in, her özelliği hasOwnProperty ile kontrol ettiğinizden emin olun .


2
Yeterli değil - bu dizi örneklerine keyfi adlandırılmış özellikleri eklemek için mükemmel Tamam, ve bunlar test edecek truedan hasOwnProperty()çek.
Sivri

İyi nokta, teşekkürler. Bunu asla bir Array'a yapacak kadar aptal olmadım, bu yüzden bunu düşünmedim!
JAL

1
@Pointy Bunu test etmedim, ancak belki de isNaNher mülk adı üzerinde bir kontrol kullanılarak aşılabilir .
WynandB

1
@ İlginç fikir; ancak basit bir sayısal dizin ile yineleme yaparken neden bu kadar değerli olduğunu gerçekten anlamıyorum.
Sivri

@WynandB yumru için üzgünüm, ama bir düzeltme sırayla hissediyorum: isNaNbir değişkenin özel değer NaN olup olmadığını kontrol etmek için, 'sayılar dışındaki şeyleri' kontrol etmek için kullanılamaz (normal bunun için typeof).
doldt

6

Mutlaka kötü değildir (ne yaptığınıza bağlı olarak), ancak diziler söz konusu olduğunda, bir şey eklenirse Array.prototype, garip sonuçlar elde edersiniz. Bu döngünün üç kez çalışmasını beklediğiniz yerde:

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

Adında bir fonksiyon varsa helpfulUtilityMethodeklendi Arrays'prototype , sonra döngü dört kez çalıştıran sona ereceğini: keyolacağını 0, 1, 2, ve helpfulUtilityMethod. Sadece tamsayılar bekliyorsanız, ayy.


6

for(var x in y)Nesneleri değil , yalnızca özellik listelerini kullanmalısınız (yukarıda açıklandığı gibi).


13
Sadece SO hakkında bir not - 'yukarıda' yoktur, çünkü yorumlar her zaman sayfadaki sıralamayı değiştirir. Yani, hangi yorumu kastettiğini gerçekten bilmiyoruz. Bu nedenle "x kişinin yorumunda" demek iyidir.
JAL

@JAL ... veya kalıcı bağlantıyı cevaba ekleyin.
WynandB

5

for...inNeden biri size söyledi tahmin edebilirsiniz, ancak bir dizi için döngü kullanmak yanlış değil:

1.) Zaten bir dizi için bu amacı olan, ancak 'forEach' olarak adlandırılan daha fazla işlevsellik ve daha az sözdizimine sahip daha yüksek bir sipariş işlevi veya yöntemi vardır: Array.prototype.forEach(function(element, index, array) {} );

2.) Diziler daima bir uzunluğa sahip, ancak for...inve forEachherhangi bir değeri için işlevi yürütmek yok'undefined' sadece tanımlanmış bir değere sahip endeksler için. Bu nedenle, yalnızca bir değer atarsanız, bu döngüler yalnızca bir kez bir işlev yürütür, ancak bir dizi numaralandırıldığından, her zaman tanımlı bir değere sahip en yüksek dizine kadar bir uzunluğa sahip olur, ancak bunları kullanırken bu uzunluk fark edilmeyebilir döngüler.

3.) Döngü standardı, bir işlevi parametrelerde tanımladığınız kadar çok sayıda yürütür ve bir dizi numaralandığından, bir işlevi kaç kez yürütmek istediğinizi tanımlamak daha mantıklıdır. Diğer döngülerden farklı olarak for döngüsü, değer tanımlanmış olsun ya da olmasın, dizideki her dizin için bir işlev yürütebilir.

Özünde, herhangi bir döngüyü kullanabilirsiniz, ancak tam olarak nasıl çalıştıklarını hatırlamanız gerekir. Farklı döngülerin yinelendiği koşulları, ayrı işlevlerini anlayın ve farklı senaryolar için az çok uygun olduklarını anlayın.

Ayrıca, forEachyöntemi kullanmak for...ingenel olarak döngüden daha iyi bir uygulama olarak düşünülebilir , çünkü yazmak daha kolaydır ve daha fazla işlevselliğe sahiptir, bu nedenle sadece bu yöntemi ve standardı kullanma alışkanlığına sahip olmak isteyebilirsiniz, ancak aramak.

Aşağıda, ilk iki döngünün yalnızca bir kez console.log deyimlerini yürütürken, döngü standardı işlevi belirtildiği kadar çok kez çalıştırır, bu durumda 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]

4

Bunun (genellikle) kötü bir uygulama olmasının nedenleri şunlardır:

  1. for...indöngüler kendi numaralandırılabilir özelliklerinin ve prototip (ler) inin numaralandırılabilir özellikleri üzerinde yinelenir. Genellikle bir dizi yinelemesinde yalnızca dizinin kendisi üzerinde yineleme yapmak isteriz. Ve diziye kendiniz bir şey eklemeseniz bile, kitaplıklarınız veya çerçeveniz bir şey ekleyebilir.

Örnek :

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...indöngüler belirli bir yineleme sırasını garanti etmez . Günümüzde çoğu modern tarayıcıda düzen görülmesine rağmen, hala% 100 garanti yoktur.
  2. for...indöngüler undefineddizi öğelerini, yani henüz atanmamış dizi öğelerini yok sayar.

Örnek :

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


2

için ... lütfen kullanarak aşağıda bu örneğe, göz JavaScript'inizin bir nesne üzerinde çalışan, ancak bir Array için, ama yine de biz yanlış bir yol olduğunu söyleyemem, ama tavsiye edilmez zaman yararlıdır için ... lütfen döngü:

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

Tamam, şimdi Array ile yapalım :

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

Sonucu aynı gördüğünüz gibi ...

Ama hiç prototip bir şeyler verelim en şey deneyelim Array ...

Array.prototype.someoneelse = "someoneelse";

Şimdi yeni bir Array () oluşturuyoruz;

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

Görüyorsunuz Someoneelse !!! ... Aslında bu durumda yeni Array nesnesi aracılığıyla döngü!

Biz kullanmaya gerek nedenlerinden biri Yani For..in dikkatle, ama her zaman böyle değil ...


2

A for ... in loop her zaman tuşları numaralandırır. Nesne özellikleri anahtarları her zaman String'dir, hatta bir dizinin dizine eklenen özellikleri:

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


0

özellikle bu soru tarafından ele alınmasa da, bir ... ile NodeList(bir querySelectorAllçağrıdan elde edileceği gibi, bunun yerine döndürülen öğeleri hiç görmediği için) yalnızca NodeList özellikleri üzerinden yineleme.

tek bir sonuç durumunda, aldım:

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

bu da neden for (node in nodes) node.href = newLink;başarısız olduğumu açıkladı .

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.