JavaScript - myArray.forEach ve for döngüsü arasındaki farklar


88

Şunları kullanmanızı öneren birçok soru gördüm:

for (var i = 0; i < myArray.length; i++){ /* ... */ }

onun yerine:

for (var i in myArray){ /* ... */ }

tutarsız yineleme nedeniyle diziler için ( buraya bakın ).


Ancak, nesne yönelimli döngüyü tercih eden bir şey bulamıyorum:

myArray.forEach(function(item, index){ /* ... */ });

Bu bana çok daha sezgisel geliyor.

Mevcut projem için, IE8 uyumluluğu önemli ve Mozilla'nın çoklu dolgusunu kullanmayı düşünüyorum , ancak bunun nasıl çalışacağından% 100 emin değilim.

  • Standart for döngüsü (yukarıdaki ilk örnek) ile modern tarayıcıların Array.prototype.forEach uygulaması arasında herhangi bir fark var mı?
  • Modern tarayıcı uygulamaları ile Mozilla'nın yukarıdakilere bağlı uygulaması arasında herhangi bir fark var mı (IE8'e özel olarak)?
  • Performans bir sorun değildir, sadece hangi özelliklerin yinelendiği ile tutarlılıktır.

6
Bu mümkün değildir breakdışına forEach. Ancak büyük bir avantaj, işlevle yeni bir kapsam oluşturmaktır. Polyfill ile herhangi bir problem yaşamazsınız (en azından ben hiç karşılaşmadım).
hgoebl

Eski IE ile yaşayabileceğiniz problemler, shimin kendisi değil, holesnerede undefinedolması gereken bozuk dizi yapıcısı / değişmez wrt ve dizi benzeri DOM nesneleri gibi sliceve hasOwnPropertywrt gibi diğer bozuk yöntemlerdir . Testlerim ve es5 shimspesifikasyonla uyumlu bu tür şimlendirilmiş yöntemler gösterdim (MDN'nin şimini test etmedi).
Xotic750

1
Ve bir fordöngüden kopmak için , bunun someiçin var.
Xotic750

1
"Bununla birlikte, nesne yönelimli döngüyü tercih eden bir şey bulamıyorum:" Ben buna işlevsel yol yerine emir kipi demeyi tercih ederim.
Memke

Array.find()İlk eşleşmeyi bulduktan sonra döngüden çıkmak için kullanabilirsiniz .
Mottie

Yanıtlar:


122

forDöngü ve forEachyöntem arasındaki en önemli fark , birincisi ile breakdöngüden çıkabilmenizdir. Simülasyonunu continue, sadece geçilen işlevden dönerek yapabilirsiniz forEach, ancak döngüyü tamamen durdurmanın bir yolu yoktur.

Bunun yanı sıra, ikisi etkili bir şekilde aynı işlevselliği gerçekleştirir. Diğer bir küçük fark, değişken kaldırma nedeniyle for döngüsündeki dizinin kapsamını (ve tüm içeren değişkenleri) içerir.

// 'i' is scoped to the containing function
for (var i = 0; i < arr.length; i++) { ... }

// 'i' is scoped to the internal function
arr.forEach(function (el, i) { ... });

Bununla birlikte, bunu forEachçok daha anlamlı buluyorum - bir dizinin her bir öğesi boyunca yineleme yapma niyetinizi temsil ediyor ve size yalnızca dizine değil öğeye de bir referans sağlıyor. Genel olarak, çoğunlukla kişisel zevkinize bağlı, ancak kullanabiliyorsanız forEach, kullanmanızı tavsiye ederim.


İki versiyon arasında, özellikle performansla ilgili birkaç önemli fark daha var. Aslında, bu jsperf testininforEach gösterdiği gibi basit for döngüsü yöntemden önemli ölçüde daha iyi performans gösterir .

Böyle bir performansın sizin için gerekli olup olmadığına karar vermek size bağlıdır ve çoğu durumda, ifade gücünün hıza göre olmasını tercih ederim. Bu hız farkı, muhtemelen bu yanıtta belirtildiği gibi seyrek dizilerde çalışırken temel döngü ile yöntem arasındaki küçük anlamsal farklılıklardan kaynaklanmaktadır .

Davranışına ihtiyacınız yoksa forEachve / veya döngüden erken çıkmanız gerekiyorsa, Lo-Dash'i _.eachalternatif olarak kullanabilirsiniz , bu da tarayıcılar arası çalışacaktır. JQuery kullanıyorsanız, aynı zamanda bir benzer sağlar $.each, her varyasyonda geri arama işlevine iletilen bağımsız değişkenlerdeki farklılıkları not edin.

( forEachPolyfill'e gelince , bu rotaya gitmeyi seçerseniz, eski tarayıcılarda sorunsuz çalışmalıdır.)


1
Kütüphane sürümlerinin eachECMA5 spesifikasyonu ile aynı şekilde davranmadığını, forEachtüm dizileri yoğun olarak ele alma eğiliminde olduklarını belirtmek gerekir ( farkında olmanız koşuluyla IE hatalarından kaçınmak için). Aksi takdirde bir "gotcha" olabilir. Referans olarak github.com/es-shims/es5-shim/issues/190
Xotic750

7
Ayrıca, siz doğru Array.prototype.somebir değer döndürene kadar veya dizi boyunca tamamen döngüye girene kadar döngü oluşturacak gibi kırmanıza izin veren bazı prototip yöntemleri vardır . Array.prototype.everybenzerdir Array.prototype.someancak yanlış bir değer döndürürseniz durur.
axelduch

Farklı tarayıcılarda ve bu tür şimlerde
Xotic750

Ayrıca, Array.prototype.forEach'i kullanmak, kodunuzu uzantının insafına bırakır. Örneğin, bir sayfaya gömülecek bir kod yazıyorsanız, Array.prototype.forEach'in üzerine başka bir şey yazılma ihtimali vardır.
Marble Daemon

2
@MarbleDaemon Bunun şansı o kadar küçük ki, etkili bir şekilde imkansız ve bu nedenle ihmal edilebilir. Üzerine yazmak, Array.prototype.forEachbazı Uyumlu olmayan versiyonu ile ki bu nedenle olmaz yardım için kaçınarak zaten pek çok kütüphaneleri kırmak potansiyeline sahip olacaktır.
Alexis Kral

12

Array.forEach'den çok daha iyi performans gösterecek özel foreach işlevinizi kullanabilirsiniz.

Bunu kodunuza bir kez eklemelisiniz. Bu, Diziye yeni işlev ekleyecektir.

function foreach(fn) {
    var arr = this;
    var len = arr.length;
    for(var i=0; i<len; ++i) {
        fn(arr[i], i);
    }
}

Object.defineProperty(Array.prototype, 'customForEach', {
    enumerable: false,
    value: foreach
});

Ardından, Array.forEach gibi her yerde kullanabilirsiniz.

[1,2,3].customForEach(function(val, i){

});

Tek fark 3 kat daha hızlı. https://jsperf.com/native-arr-foreach-vs-custom-foreach

GÜNCELLEME: Yeni Chrome sürümünde .forEach () performansı iyileştirildi. Ancak çözüm, diğer tarayıcılarda ek performans sağlayabilir.

JSPerf


4

Pek çok geliştirici (örneğin, Kyle Simpson) tarafından .forEach, dizinin bir yan etkiye sahip olacağını ve .mapsalt işlevler için kullanılacağını önermektedir . fordöngüler, bilinen sayıda döngü için genel amaçlı bir çözüm olarak veya programlama dillerinin çoğunda geniş desteği nedeniyle iletişim kurması daha kolay olduğu için uymayan diğer durumlar için iyi bir uyum sağlar.

Örneğin

/* For Loop known number of iterations */
const numberOfSeasons = 4;
for (let i = 0; i < numberOfSeasons; i++) {
  //Do Something
}

/* Pure transformation */
const arrayToBeUppercased = ['www', 'html', 'js', 'us'];
const acronyms = arrayToBeUppercased.map((el) => el.toUpperCase));

/* Impure, side-effects with .forEach */
const acronymsHolder = [];
['www', 'html', 'js', 'us'].forEach((el) => acronymsHolder.push(el.toUpperCase()));

Konvansiyon bilge, bu en iyi görünüyor, ancak topluluk yeni yineleme protokolü for indöngüleri üzerine bir sözleşmeye gerçekten karar vermedi . Genel olarak, JS topluluğunun benimsemeye açık göründüğü FP kavramlarını takip etmenin iyi bir fikir olduğunu düşünüyorum.

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.