İçin JavaScript… in vs for


461

Sizce ilmekler için ve ilmekler arasında büyük bir fark var mı? Ne tür bir "for" kullanmayı tercih edersiniz ve neden?

Diyelim ki bir dizi ilişkisel dizimiz var:

var myArray = [{'key': 'value'}, {'key': 'value1'}];

Böylece yineleyebiliriz:

for (var i = 0; i < myArray.length; i++)

Ve:

for (var i in myArray)

Çok büyük bir fark görmüyorum. Performans sorunu var mı?


13
Not biz de, o JS 1.6 itibariyle , sahip: myArray.forEach(callback[, thisarg]).
Benji XVI

14
@Benji array.forEach aslında ES5'te.
mikemaccana

2
bir for-in döngüsünde şuna benzer bir koşul gerekir:if(myArray.hasOwnProperty(i)){true}
Eric Hodonsky

6
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); IE8 dışında hemen hemen her yerde kullanmakta sorun yok - ve en zarif sözdizimi
Jon z

5
ECMAScript 6'da da bir for...ofaçıklama var, örneğin:for (let i of myArray) console.log(i);
Vitalii Fedorenko

Yanıtlar:


548

Seçim hangi deyimin en iyi anlaşıldığına dayanmalıdır.

Bir dizi aşağıdakiler kullanılarak yinelenir:

for (var i = 0; i < a.length; i++)
   //do stuff with a[i]

İlişkilendirilebilir dizi olarak kullanılan bir nesne aşağıdakiler kullanılarak yinelenir:

for (var key in o)
  //do stuff with o[key]

Toprak paramparça nedenleriniz olmadığı sürece, belirlenmiş kullanım şekline sadık kalın.


38
Şunu belirtmek gerekir ki, if ifadesi filtrelemede ... için kullanmanın iyi bir uygulama olduğu söylenebilir. Yineleyici tarafından döndürülen bir üyenin gerçekten nesnenin üyesi olup olmadığını kontrol eden kullanışlı bir "obj.hasOwnProperty (member)" nesnesi yöntemi vardır. Bkz: javascript.crockford.com/code.html
Damir Zekić

57
Başka bir yanıt (lar) da yorumlandığı gibi, "for ... in" Diziler için düzgün çalışmaz, çünkü tüm Array özellikleri ve yöntemleri üzerinde yineleme yapar. Bu nedenle, "for ... in" öğesini yalnızca nesne özellikleri üzerinde yineleme yapmak için kullanmalısınız. Aksi takdirde, "için (i = 0; i <bir şey; i ++)"
Denilson Sá Maia

Performans nedenleriyle, IMO for, döngüdeki her seferinde bir uzunluk uzunluğunu değerlendirmek yerine dizinin uzunluğunu değerlendirmek daha iyidir .
UpTheCreek

9
@UpTheCreek: Dizi aslında HTMLDOM tarafından döndürülen bir şey olduğunda kesinlikle doğrudur, ancak, kayda değer bir fark görmeden önce standart bir javascript dizisinin ne kadar büyük olması gerektiğini merak ediyorum? Şahsen kodu farklı bir şey yapmak için gerekli olduğu kanıtlanıncaya kadar mümkün olduğunca basit tutacağım.
AnthonyWJones

2
@Pichan Bence for-döngüsü durumunuzda i < ldeğil demek istediniz i < a.
Max Nanasy

161

Douglas Crockford , ifadeyi kullanmamak için JavaScript: İyi Parçalar'ı (sayfa 24) önermektedir for in.

Bir for innesnedeki özellik adlarının üzerine döngü yapmak için kullanılırsa , sonuçlar sıralanmaz. Daha da kötüsü: Beklenmedik sonuçlar alabilirsiniz; prototip zincirinden miras alınan üyeleri ve yöntemlerin adını içerir.

Özellikleri hariç her şey ile filtrelenebilir .hasOwnProperty. Bu kod örneği, muhtemelen başlangıçta istediğinizi yapar:

for (var name in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, name)) {
        // DO STUFF
    }
}

70
for ... in, nesne özellikleri üzerinde döngü için mükemmel bir şekilde uygundur. Dizi öğeleri üzerinde döngü için uygun değildir. Bu senaryolar arasındaki farkı anlayamıyorsanız, evet, kaçınmalısınız ... in; aksi takdirde, çıldırmak.
Shog9

8
SİPARİŞ EDİLMEDİ gerçeğini vurgulamak istiyorum! Bu büyük bir sorun olabilir ve yakalamak zor bir hata olurdu.
Jason

4
+1, "prototip zincirinden ve yöntemlerin adından devralınan üyeleri içerir." Örneğin birisi kodunuzu Prototip yüklendiğinde (kodunuz gerçekten kullanmasa bile) kullanırsa eğlenirsiniz.
ijw

13
Unutmayın lütfen beyanname değişken: for(var name in object)...bu kod örneğin bir işlev içinde ise, aksi halde, nameküresel nesne (bir bir atamanın bir özellik olarak sona değişken uç bildirilmemiş tanımlayıcı da yeni ECMAScript'te, yaptığı) 5 Katı Mod, bu kod bir atar ReferenceError.
CMS

6
@Nosredna: John Resig dışında WontFix olarak işaretlenmiş Chrome için yineleme sırası ile ilgili bir sorun var. code.google.com/p/chromium/issues/detail?id=883 . Chrome'dan önce bile, tekrar bir mülk kaldırıp eklerseniz yineleme sırası tarayıcılarda aynı değildi. Ayrıca IE 9 krom gibi davranır (sözde hız iyileştirmeleri için). Yani ... Lütfen yanlış bilgi yaymayı bırakın, buna bağlı olarak çok naif olursunuz.
Juan Mendes

62

FYI - jQuery Kullanıcıları


jQuery'nin each(callback)yöntemi for( ; ; )varsayılan olarak döngü kullanır ve for( in ) yalnızca uzunluk ise kullanılır undefined.

Bu nedenle, bu işlevi kullanırken doğru sırayı almanın güvenli olduğunu söyleyebilirim.

Örnek :

$(['a','b','c']).each(function() {
    alert(this);
});
//Outputs "a" then "b" then "c"

Bunu kullanmanın dezavantajı, bazı UI olmayan mantık yapıyorsanız, işlevlerinizin diğer çerçevelere daha az taşınabilir olacağıdır. each()Fonksiyon muhtemelen en iyi jQuery seçicileri ile kullanım için ayrılmıştır ve for( ; ; )aksi tavsiye edilebilir.



4
Her zaman var documentcloud.github.com/underscore _.each ve diğer yararlı fonksiyonları bir sürü olan
w00t

1
Ayrıca nesnemde length özelliği varsa $ .each başarısız olur? örneğin x = {a: "1", b: "2", uzunluk: 3}.
Onur Topal

29

ne tür bir döngü kullandığınıza ve hangi tarayıcıya bağlı olarak performans farklılıkları vardır.

Örneğin:

for (var i = myArray.length-1; i >= 0; i--)

bazı tarayıcılarda neredeyse iki kat daha hızlıdır:

for (var i = 0; i < myArray.length; i++)

Ancak dizileriniz BÜYÜK veya sürekli döngü yapmadıkça hepsi yeterince hızlıdır. Dizi döngüsünün projenizdeki bir darboğaz olduğundan (veya bu konudaki herhangi bir projeden) ciddi olarak şüpheliyim


5
Döngüden önce "myArray.length" ifadesinin bir değişkene depolanması performans farkını ortadan kaldırır mı? Benim tahminim "evet".
Tomalak

3
Hayır. 'MyArray.length' bir nesne üzerinde bir yöntem değil, bir özelliktir - değerini belirlemek için hiçbir hesaplama yapılmaz. Değerini bir değişkente saklamak hiçbir şey yapmaz.
Jason

3
Evet var. Özellikler değişken değildir; onlar get / set kodu var.
ste

12
Kullanmaya meyillifor(var i = myArray.length; i--;)
Kevin

2
Netlik ve okunabilirlik kodu. Şu anda saçma büyük diziler kullanırken bazı tarayıcılarda marjinal olarak daha hızlı çalışacak olanlar için değil. Optimizasyonlar değişebilir, ancak kodunuzun okunabilirliği (veya eksikliği) değişmez. Başkalarının kolayca takip edebileceği bir kod yazın ve optimize edicilerin zamanlarını yakalamalarına izin verin.
aroth

26

Yerel Array.forEach yönteminin artık yaygın olarak desteklendiğini unutmayın .


2
Bu ne işe yarıyor? Diğer yazılarda (dizi öğeleri yerine özellikler üzerinde döngü) belirtilen sorunları var mı? Ayrıca, IE8 desteklemediğinden, yaygın olarak desteklendiğini söylemek biraz zor.
Rauni Lillemets

2
kadar * nix kullanıcıları onu hor, IE8 tüm alt-windows7 kullanıcılarının çoğu. bu tarayıcı pazarının büyük bir kısmı.
sbartell

2
@Rauni - Demek istediğim, ancak masaüstü cihazlar için IE tarayıcı payı% 40'tan az, en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table ve markethare.hitslink.com/… ve diğer sitelere göre, tarayıcıların en az% 8'i IE 9'dur. Başka bir deyişle, Array.forEach masaüstü tarayıcılarının yaklaşık% 70'i tarafından desteklenmektedir, bu yüzden 'yaygın olarak desteklenen' mantıksız olduğunu düşünmüyorum. Kontrol etmedim, ancak mobil destek (WebKit ve Opera tarayıcılarında) daha da yüksek olabilir. Açıkçası, coğrafi olarak önemli farklılıklar vardır.
Sam Dutton

1
Güncelleme için teşekkürler. Bunun "yaygın olarak desteklendiği" söylenebileceğini kabul ediyorum. Tek sorun, kullanıcı bu JS yöntemini kullanıyorsa, desteklenmiyorsa yine de dava için bir yedekleme yöntemi yazmak zorunda olmasıdır ..
Rauni Lillemets

1
@Rauni - otomatik olarak yedekleme yöntemleri sağlamak için es5-shim ve es6-shim kullanabilirsiniz. github.com/es-shims/es5-shim
Michiel van der

24

2012 için cevap güncellendiTüm önemli tarayıcıların güncel sürümü - Chrome, Firefox, IE9, Safari ve Opera ES5'in yerel dizisini desteklemektedir. ForEach.

IE8'i yerel olarak desteklemek için bir nedeniniz yoksa (uygun bir JS ortamı sağlayacak bu kullanıcılara ES5-shim veya Chrome çerçevesinin sağlanabileceğini unutmayın), dilin uygun sözdizimini kullanmak daha temizdir:

myArray.forEach(function(item, index) {
    console.log(item, index);
});

Array.forEach () için tüm belgeler MDN adresindedir.


1
Geri
aramanın

Söylediklerinizi duyuyorum, ama bu durumda onu basitleştirmek tüm olasılıkları gizliyor. Hem indekse hem de değere sahip olmak, anahtarlar veya değerler üzerinde hangisinin yinelendiğini hatırlamak zorunda olmadığınız bonus ile ... hem içinde hem de her için ... yerine kullanılabileceği anlamına gelir.
drewish

1
@Cory: ES5 forEach, eski ES3 tarayıcılarına oldukça kolay bir şekilde eklenebilir. Daha az kod daha iyi koddur.
mikemaccana

2
@nailer Bu, dizilerde ve nesnelerde birbirinin yerine kullanılabilir mi?
hitautodestruct

1
@hitautodestruct Array'ın prototipinin bir parçası, Object'in değil. Genellikle toplulukta şu anda Dizi olmayan nesneler hala 'for (object in var key) {}' ile yinelenmektedir.
mikemaccana

14

Dizi seyrek olduğunda ikisi aynı değildir.

var array = [0, 1, 2, , , 5];

for (var k in array) {
  // Not guaranteed by the language spec to iterate in order.
  alert(k);  // Outputs 0, 1, 2, 5.
  // Behavior when loop body adds to the array is unclear.
}

for (var i = 0; i < array.length; ++i) {
  // Iterates in order.
  // i is a number, not a string.
  alert(i);  // Outputs 0, 1, 2, 3, 4, 5
  // Behavior when loop body modifies array is clearer.
}

14

Prototip zincirini atlamak için forEach kullanma

@ Nailer'ın yukarıdaki cevabına hızlı bir ek olarak sadece forEach ile Object.keys kullanmak, hasOwnProperty kullanmak zorunda kalmadan prototip zinciri üzerinde yineleme yapmaktan kaçınabileceğiniz anlamına gelir.

var Base = function () {
    this.coming = "hey";
};

var Sub = function () {
    this.leaving = "bye";
};

Sub.prototype = new Base();
var tst = new Sub();

for (var i in tst) {
    console.log(tst.hasOwnProperty(i) + i + tst[i]);
}

Object.keys(tst).forEach(function (val) {
    console.log(val + tst[val]);
});

2
Kahretsin, sinsi. Buna ulaşmak için diğer 50 yazıya değdi. obj = {"pembe": "ördekler", kırmızı: "kazlar"}; Object.keys (obj) === ["pembe", "kırmızı"]
Orwellophile

14

İkincisi, yineleme yöntemini ihtiyacınıza göre seçmeniz gerektiğine inanıyorum. Ben aslında seni öneririm hiç yerli döngü Arrayile for inyapısı. Çok daha yavaş ve Chase Seibert'in şimdiye kadar işaret ettiği gibi Prototip çerçevesiyle uyumlu değil.

JavaScript ile çalışıyorsanız mutlaka göz atmanız gereken farklı döngü stilleri üzerinde mükemmel bir kıyaslama vardır . Erken optimizasyonlar yapmayın, ancak bu şeyleri başınızın arkasında bir yerde tutmalısınız.

for inBir nesnenin tüm özelliklerini almak için kullanırım , bu da özellikle komut dosyalarınızda hata ayıklama yaparken kullanışlıdır. Örneğin, bilmediğim bir nesneyi keşfettiğimde bu çizginin kullanışlı olmasını seviyorum:

l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.log(l);

Tüm nesnenin içeriğini (yöntem gövdeleriyle birlikte) Firebug günlüğüme döker. Çok kullanışlı.


Bağlantı kesildi. Birisi başka bir bağlantıya sahipse, elbette karşılaştırmayı görmek ister.
Billbad

Artık kırık değil.
Olli

Her döngü döngü prototip kırmak? Şimdi yaygın olarak desteklendiği için, bu prototipin çözmesi gereken bir şeydir.
mvrak

7

İşte yaptığım bir şey.

function foreach(o, f) {
 for(var i = 0; i < o.length; i++) { // simple for loop
  f(o[i], i); // execute a function and make the obj, objIndex available
 }
}

bunu nasıl kullanacağınız bu
diziler ve nesneler üzerinde çalışacaktır (HTML öğelerinin bir listesi gibi)

foreach(o, function(obj, i) { // for each obj in o
  alert(obj); // obj
  alert(i); // obj index
  /*
    say if you were dealing with an html element may be you have a collection of divs
  */
  if(typeof obj == 'object') { 
   obj.style.marginLeft = '20px';
  }
});

Bunu yeni yaptım, bu yüzden önerilere açığım :)


Harika şeyler - oldukça basit!
Chris

6

Öğelere nasıl başvurmak istediğime bağlı olarak farklı yöntemler kullanırdım.

Sadece geçerli öğeyi istiyorsanız foreach kullanın.

Göreli karşılaştırmalar yapmak için bir dizinleyiciye ihtiyacınız varsa kullanın. (Yani bu önceki / sonraki öğeyle nasıl karşılaştırılır?)

Performans farkı hiç fark etmedim. Endişelenmeden önce bir performans sorunu yaşamayı beklerdim.


Aşağıdaki Bnos cevabına bakın - çünkü ... burada beklediğiniz şeyi yapmıyor ve eğer kullanırsanız her türlü eğlenceye sahip olabilirsiniz. Kayıt için, Prototip işleri doğru şekilde yapar.
marcus.greasly

4

İle (myArray içinde var i) için , nesneleri üzerinde döngü de olabilir ben anahtar adını içerecek ve üzeri özelliğine erişebilir [i] myArray . Doğrudan nesne prototipleri yöntemleri eklerseniz jQuery veya prototip gibi herhangi bir dış çerçeve kullanırsanız Ek olarak, nesnenin eklemiş olacak herhangi yöntemleri, yani çok döngü içinde yer alacak, ya da, bir noktada i işaret eder bu yöntemler.


4

Dikkat et!

Birden fazla komut dosyası etiketiniz varsa ve örneğin etiket niteliklerinde bir bilgi arıyorsanız, basit bir dizi değil, bir HTMLCollection nesnesi olduğu için .length özelliğini for döngüsüyle kullanmanız gerekir.

https://developer.mozilla.org/en/DOM/HTMLCollection

Foreach ifadesini (listenizdeki var i) için kullanırsanız, çoğu tarayıcıda HTMLCollection öğesinin özelliklerini ve yöntemlerini döndürür!

var scriptTags = document.getElementsByTagName("script");

for(var i = 0; i < scriptTags.length; i++)
alert(i); // Will print all your elements index (you can get src attribute value using scriptTags[i].attributes[0].value)

for(var i in scriptTags)
alert(i); // Will print "length", "item" and "namedItem" in addition to your elements!

GetElementsByTagName bir NodeList döndürse bile, çoğu tarayıcı bir HTMLCollection döndürüyor: https://developer.mozilla.org/en/DOM/document.getElementsByTagName


3

Çünkü Dizilerdeki döngüler Prototip ile uyumlu değildir. Gelecekte bu kütüphaneyi kullanmanız gerekebileceğini düşünüyorsanız, döngülere bağlı kalmak mantıklı olacaktır.

http://www.prototypejs.org/api/array


Unutmayın "bu kütüphaneyi kullanmanız gerekebilir". Bunun yerine "JS'niz bu kütüphaneyi kullanan başka herhangi bir şeye dahil edilmiş olabilir" diye düşünün , çünkü sorunlar hala devam etmektedir.
ijw

3

Nesneleri, prototip ve dizileri kullanarak "her biri için" sorunları gördüm

benim anlayış, her biri için nesnelerin özellikleri ve DEĞİL diziler içindir


3

Kodunuzu gerçekten hızlandırmak istiyorsanız, buna ne dersiniz?

for( var i=0,j=null; j=array[i++]; foo(j) );

for ifadesinin içinde while mantığına sahip olmak ve daha az gereksizdir. Ayrıca firefox'ta Array.forEach ve Array.filter var


2
Bu neden kodunuzu hızlandıracak? Bunun gibi ifadelerin neden yeniden sıralanmasını hızlandıracağını göremiyorum.
Rup

3

Jsperf'e göre daha kısa ve en iyi kod

keys  = Object.keys(obj);
for (var i = keys.length; i--;){
   value = obj[keys[i]];// or other action
}

1

Paralellikten yararlanmak için Array (). ForEach döngüsünü kullanın


4
Tarayıcıdaki JavaScript olay döngüsü eşzamanlıdır, bu nedenle Array.prototype.forEachparalel olarak geri aramaya birden fazla çağrı gerçekleştirmez.
Mike Samuel

1

(;;) için içindir Diziler : [20,55,33]

For..in için Nesneleri : {x: 20, y: 55: z: 33}


0

Dikkatli ol!!! Mac OS'de Chrome 22.0 kullanıyorum ve her sözdizimi için sorun yaşıyorum.

Bu bir tarayıcı sorunu, javascript sorunu veya kodda bazı hata olup olmadığını bilmiyorum, ama ÇOK garip. Nesnenin dışında mükemmel çalışır.

var MyTest = {
    a:string = "a",
    b:string = "b"
};

myfunction = function(dicts) {
    for (var dict in dicts) {
        alert(dict);
        alert(typeof dict); // print 'string' (incorrect)
    }

    for (var i = 0; i < dicts.length; i++) {
        alert(dicts[i]);
        alert(typeof dicts[i]); // print 'object' (correct, it must be {abc: "xyz"})
    }
};

MyObj = function() {
    this.aaa = function() {
        myfunction([MyTest]);
    };
};
new MyObj().aaa(); // This does not work

myfunction([MyTest]); // This works

0

İkisi arasında önemli bir fark vardır. For-in, bir nesnenin özellikleri üzerinde yinelenir, bu nedenle vaka bir dizi olduğunda yalnızca öğeleri üzerinde değil aynı zamanda sahip olduğu "kaldır" işlevi üzerinde de yinelenir.

for (var i = 0; i < myArray.length; i++) { 
    console.log(i) 
}

//Output
0
1

for (var i in myArray) { 
    console.log(i) 
} 

// Output
0 
1 
remove

For-in'i bir ile kullanabilirsiniz if(myArray.hasOwnProperty(i)). Yine de, diziler üzerinde yineleme yaparken her zaman bundan kaçınmayı ve sadece for (;;) ifadesini kullanmayı tercih ederim.


0

Her ikisi de çok benzer olsa da küçük bir fark var:

var array = ["a", "b", "c"];
array["abc"] = 123;
console.log("Standard for loop:");
for (var index = 0; index < array.length; index++)
{
  console.log(" array[" + index + "] = " + array[index]); //Standard for loop
}

bu durumda çıktı:

DÖNGÜ İÇİN STANDART:

ARRAY [0] = A

ARRAY [1] = B

ARRAY [2] = C

console.log("For-in loop:");
for (var key in array)
{
  console.log(" array[" + key + "] = " + array[key]); //For-in loop output
}

bu durumda çıktı:

FOR-IN DÖNGÜ:

ARRAY [1] = B

ARRAY [2] = C

ARRAY [10] = D

ARRAY [ABC] = 123


0

For in ifadesi, bir nesnenin tüm özelliklerinin adlarında döngü oluşturmayı sağlar. Ne yazık ki, prototip zincirinden miras kalan tüm üyeler arasında da dolaşıyor. Bu, veri üyelerine ilgi duyulduğunda yöntem işlevlerini sunmanın kötü yan etkisine sahiptir.

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.