javascript forEach yönteminin ne faydası var (bu harita yapamaz)?


91

Map ve foreach'de gördüğüm tek fark map, bir dizi döndürmek ve döndürmemek forEach. Ancak, forEach" func.call(scope, this[i], i, this);" yönteminin son satırını bile anlamıyorum . Örneğin, "değil this" ve " scope" aynı nesneye atıfta olup this[i]ve idöngüde akım değerine atıfta?

Başka bir gönderide birisinin " forEachListenin her bir öğesi temelinde bir şeyler yapmak istediğinizde kullanın . Örneğin sayfaya bir şeyler ekliyor olabilirsiniz. Esasen," yan etkiler "istediğiniz zamanlar için harikadır. Yan etkilerin ne anlama geldiğini bilmiyorum.

Array.prototype.map = function(fnc) {
    var a = new Array(this.length);
    for (var i = 0; i < this.length; i++) {
        a[i] = fnc(this[i]);
    }
    return a;
}

Array.prototype.forEach = function(func, scope) { 
    scope = scope || this; 
    for (var i = 0, l = this.length; i < l; i++) {
        func.call(scope, this[i], i, this); 
    } 
}

Son olarak, bu yöntemlerin javascript'te (bir veritabanını güncellemediğimiz için) aşağıdaki gibi sayıları değiştirmek dışında gerçek kullanımları var mı?

alert([1,2,3,4].map(function(x){ return x + 1})); //this is the only example I ever see of map in javascript.

Herhangi bir cevap için teşekkürler.


mapVe ile yaptığınız gibi bir işlevin yerel JavaScript tanımı nasıl bulunur forEach? Google'dan aldığım tek şey kullanım özellikleri ve eğitimler.
WoodrowShigeru

Dilden bağımsız olana da bakın foreach ve harita arasında bir fark var mı?
Bergi

Yanıtlar:


95

Örneğinizdeki mapve forEacharasındaki temel fark forEach, orijinal dizi öğeleri üzerinde çalışıp mapsonuç olarak açıkça yeni bir dizi döndürmesidir.

Bununla forEachbirlikte, orijinal dizideki her bir öğeyle ilgili bazı işlemler yapıyorsunuz ve isteğe bağlı olarak değiştiriyorsunuz. forEachYöntem her bir öğenin sağlamak işlevini çalışır, ancak hiçbir şey döndürür ( undefined). Öte yandan, mapdizi boyunca ilerler, her öğeye bir işlev uygular ve sonucu yeni bir dizi olarak yayınlar .

İle "yan etki" forEach, orijinal dizinin değiştirilmesidir. İle "yan etki" mapidiomatic kullanımında, orijinal dizi elemanları vardır, aracı olup değiştirildi; yeni dizi, orijinal dizideki her öğenin bire bir eşlemesidir - eşleme dönüşümü sağladığınız işlevdir.

Veritabanının bulunmadığı gerçeği, veri yapıları üzerinde işlem yapmak zorunda kalmayacağınız anlamına gelmez; bu, sonuçta, herhangi bir dilde programlamanın özlerinden biridir. Son sorunuza gelince, diziniz yalnızca sayıları değil, nesneleri, dizeleri, işlevleri vb. İçerebilir.


4
gelecekten not: bu cevap aslında saçmadır; .mapyapabileceği her şeyi .forEachyapabilir (öğeleri değiştirmek dahil), yalnızca yineleyici işlevinden oluşturulmuş yeni bir liste döndürür.
Travis Webb

6
Geçmişten gelen yanıt: Saygıyla katılmıyorum. .map()yeni bir dizi oluşturur ve orijinali değiştirmez. Gerçekten de eşlenmekte olan diziyi değiştirebilirsiniz, ancak bu, saçma değilse de en azından deyimsel olmayacaktır. .forEach(), benzer olsa da, işlevini her öğeye uygular, ancak her zaman döndürür undefined. Yukarıdakilerin hepsine rağmen, OP aynı zamanda dizi prototipine eklenen kendi özel işlevlerini de sormaktadır; geçmişte burada ES5 yoktu.
Ken Redler

4
Hala forEachyapamayacağın hiçbir şey yok map. Getirdiği değeri umursamazsınız mapve bir forEach. Aradaki fark, mapsonuçlara göre yeni bir dizi oluşturmak istemediğiniz görevler için kullanmanın süper etkisiz olmasıdır . Yani onlar için kullanıyorsun forEach.
dürtmek

2
@poke: .. ve aynı şekilde, düz eski bir fordöngü ile ihtiyacınız olan her şeyi yapabilirsiniz . "Etkililikte" bazı farklılıklar olduğu konusunda hemfikir değilim, ama aynı zamanda kimsenin birinin bir sihire sahip olduğunu diğerinin bir şekilde başaramayacağını iddia ettiğini sanmıyorum. Aslında mapyapamayacak bir şey olmasına rağmen : bir dizi döndürmemek. JS olarak gibi fonksiyonel sık davranışına diğer dillerden beklentileri canlı sahip olmanın değeri var map, reduce, filtervb Örneğin, hangisini uygulayabilirsiniz reduceRightbenzer yapı taşlarını kullanarak, ama neden sadece kullanmayın reduceRight?
Ken Redler

1
@ChinotoVokro, örneğiniz, döndürülen nesnenin içine atayarak orijinal diziyi açıkça değiştiriyor . Söylediğimiz kesin şey gerçekten mümkün ama kafa karıştırıcı ve deyimsel değil. Normalde yeni değeri döndürürsünüz, onu orijinal diziye de b.val = b.val**2 a=[{val:1},{val:2},{val:3},{val:4}]; a.map((b)=>{b.val**2}); JSON.stringify(a);
atamazsınız

66

İki yöntem arasındaki temel fark kavramsal ve üslup şudur: kullanmak forEachbir şeyler yapmak istediğinizde için ya birlikte bir dizinin her öğesi ( "ile" yapıyor siz "yan etkileri" ile kastedilen cite sonrası, sanırım budur) , kullanmak oysa mapistediğiniz zaman kopyalayıp dönüşümü (orijinali değiştirmeden) bir dizinin her öğesi.

Hem Çünkü mapve forEachbir dizideki her öğe üzerinde bir işlevi çağırmak ve bu fonksiyon kullanıcı tanımlı olduğu, bir tanesinde değil diğeriyle ile yapabileceğiniz hemen hemen hiçbir şey yoktur. Çirkin olsa da, mapbir diziyi yerinde değiştirmek ve / veya dizi öğeleriyle bir şeyler yapmak için kullanmak mümkündür :

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.map(function(el) {
    el.val++; // modify element in-place
    alert(el.val); // do something with each element
});
// a now contains [{ val: 2 }, { val: 3 }, { val: 4 }]

ancak kullanım amacınız açısından çok daha net ve daha açık forEach:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.forEach(function(el) { 
    el.val++;
    alert(el.val);
});

Özellikle, gerçek dünyada genellikle olduğu gibi, elfaydalı bir şekilde insan tarafından okunabilir bir değişkense:

cats.forEach(function(cat) { 
    cat.meow(); // nicer than cats[x].meow()
});

Aynı şekilde, kolayca forEachyeni bir dizi oluşturmak için kullanabilirsiniz :

var a = [1,2,3],
    b = [];
a.forEach(function(el) { 
    b.push(el+1); 
});
// b is now [2,3,4], a is unchanged

ama kullanımı daha temiz map:

var a = [1,2,3],
    b = a.map(function(el) { 
        return el+1; 
    });

Ayrıca, mapyeni bir dizi oluşturduğu için, özellikle büyük diziler için ihtiyacınız olan tek şey yineleme olduğunda en azından bir miktar performans / bellek isabeti yaşayacağını unutmayın - bkz. Http://jsperf.com/map-foreach

Bu işlevleri neden kullanmak istediğinize gelince, javascript'te dizi manipülasyonu yapmanız gerektiğinde faydalıdırlar, ki bu (bir tarayıcı ortamında sadece javascript hakkında konuşsak bile) oldukça sık, neredeyse her zaman kodunuzda elle yazmadığınız bir diziye erişiyorsunuz. Sayfadaki bir dizi DOM öğesi veya bir AJAX isteğinden çekilen veriler veya kullanıcı tarafından bir forma girilen verilerle uğraşıyor olabilirsiniz. Karşılaştığım yaygın bir örnek map, verileri istediğiniz biçime dönüştürmek için kullanmak isteyebileceğiniz ve daha sonra forEachkullanıcınıza görüntülemek için yeni dizinizi yinelemek için kullanabileceğiniz harici bir API'den veri çekmektir.


.map()inline elemanları değiştirmek için sürekli kullanan insanlar görüyorum . .map.forEach
2016

1
@chovy Bir dizi sonuç yapmak isteyip istemediğinize göre seçim yapmanız gerektiğine inanıyorum. .mapöğeleri değiştirebilir, evet, ancak ihtiyacınız olmayan bir dizi oluşturacaktır. Ayrıca birini veya diğerini seçmenin, okuyucunun yan
etkinizin

16

Oylanan cevap (Ken Redler'den) yanıltıcıdır.

Bilgisayar bilimindeki bir yan etki , bir işlevin / yöntemin bir özelliğinin küresel bir durumu [wiki] değiştirmesi anlamına gelir . Dar anlamda, bu argümanlardan ziyade küresel bir durumdan okumayı da içerebilir. Zorunlu veya OO programlamada, çoğu zaman yan etkiler ortaya çıkar. Ve muhtemelen farkında olmadan onu kullanıyorsunuz.

Arasındaki anlamlı fark forEachve mapolmasıdır mapederken, ayırır hafıza ve depolar dönen değer forEachuzağa fırlatır. Daha fazla bilgi için emca özelliklerine bakın .

İnsanların forEachbir yan etki istediğinizde kullanıldığını söylemesinin nedeni ise, geri dönüş değerinin forEachher zaman olmasıdır undefined. Yan etkisi yoksa (genel durumu değiştirmezse), işlev yalnızca cpu zamanını boşa harcıyor demektir. Optimize edici bir derleyici, bu kod bloğunu ortadan kaldıracak ve onu son değerle ( undefined) değiştirecektir.

Bu arada, JavaScript'in yan etkiler konusunda herhangi bir kısıtlaması olmadığı unutulmamalıdır. Yine de içindeki orijinal diziyi değiştirebilirsiniz map.

var a = [1,2,3]; //original
var b = a.map( function(x,i){a[i] = 2*x; return x+1} );
console.log("modified=%j\nnew array=%j",a,b);
// output:
// modified=[2,4,6]
// new array=[2,3,4]

2
Bu soru ve cevapları ve yorumları her iki yılda bir geri dönmek için her zaman eğlencelidir. Haritanın JS'de uygulanma biçimi, bellek ayırma açısından başka bir güzel açıdır.
Ken redler

7

Bu beklenmedik bir cevabı olan güzel bir sorudur.

Aşağıdakiler resmi açıklamasına dayanmaktadırArray.prototype.map() .

Orada hiçbir şeyforEach() yapabilir map()kutu değil. Olduğunu, map()bir olduğunu sıkı süper seti arasında forEach().

Her ne kadar map()genellikle yeni bir dizi oluşturmak için kullanılır, olabilir de geçerli diziyi değiştirmek için kullanılabilir. Aşağıdaki örnek bunu göstermektedir:

var a = [0, 1, 2, 3, 4], mapped = null;
mapped = a.map(function (x) { a[x] = x*x*x; return x*x; });
console.log(mapped); // logs [0, 1, 4, 9, 16]  As expected, these are squares.
console.log(a); // logs [0, 1, 8, 27, 64] These are cubes of the original array!!

Yukarıdaki örnekte, auygun bir şekilde ayarlandı a[i] === iiçin i < a.length. Öyle bile olsa map(), çağrıldığı dizinin gücünü ve özellikle de değiştirme yeteneğini gösterir.

Not 1:
Resmi açıklama , çağrıldığı dizinin uzunluğununmap() bile değişebileceğini ima eder ! Ancak bunu yapmak için (iyi bir) neden göremiyorum.

Not 2:
iken map()haritası bir süper dizi forEach(), forEach()tek değiştireceğim verilen bir dizi arzuları hala kullanılmalıdır. Bu, niyetlerinizi netleştirir.

Umarım bu yardımcı olmuştur.


8
Aslında, forEach'in yapabileceği bu haritanın yapamayacağı bir şey var - bir dizi döndürmemek.
Antti Haapala

1
Kapsamlı değişken yerine, hedef diziyi mutasyona uğratmak için eşleme işlevinin üçüncü bağımsız değişkenini de kullanabilirsiniz:mapped = a.map(function (x, i, arr) { arr[i] = x * x * x; return x * x; });.
pdoherty926

@ pdoherty926 doğru, ancak bu, bir diziden diğerine harita değerlerinin aksine her orijinal öğeyi mutasyona uğratmaka.forEach(function(x, i, arr) { ... istediğinizde kavramsal olarak daha doğru bir yoldur
conny

@conny: Kabul edildi. Ayrıca, lütfen benzer bir soru için bu yanıta bakın .
Sumukh Barve

3

Sen kullanabilirsiniz mapo sanki forEach.

Ancak yapması gerekenden fazlasını yapacak.

scopekeyfi bir nesne olabilir; hiçbir şekilde zorunlu değildir this.

Orada gerçek kullanımları olup olmadığını gelince mapve forEachgerçek kullanımları varsa, sıra sormak forveya whiledöngüler.


Ama neden burada hem "kapsam" hem de "bu" çağrılıyor: func.call (kapsam, bu [i], i, bu); Kapsam, "this" olan mevcut nesneye eşit bir parametre değil mi?
JohnMerlino

Hayır, mevcut nesneye eşit olabilir . Nesnenin kendisi diziye üçüncü parametre olarak aktarılır. scope = scope || this"eğer scopeyanlış ise (tanımsız, boş, yanlış, vb.) thisbunun yerine kapsamı ayarla ve devam et" anlamına gelir.
wombleton

Buna eşit olmadığında beni bir örneğe bağlayabilir misin?
JohnMerlino

developer.mozilla.org/en/Core_JavaScript_1.5_Reference/… "Bir dizinin içeriğini bir nesne yöntemiyle yazdırma" altında bir tane var
wombleton

1

Önceki tüm sorular doğru olsa da kesinlikle farklı bir ayrım yapacağım. Kullanımı mapve forEachniyet ima edebilir.

mapMevcut verileri bir şekilde dönüştürürken kullanmayı seviyorum (ancak orijinal verilerin değişmediğinden emin olmak istiyorum.

forEachKoleksiyonu yerinde değiştirirken kullanmayı seviyorum .

Örneğin,

var b = [{ val: 1 }, { val: 2 }, { val: 3 }];
var c = b.map(function(el) {
    return { val: el.val + 1 }; // modify element in-place
});
console.log(b);
//  [{ val: 1 }, { val: 2 }, { val: 3 }]
console.log(c);
//  [{ val: 3 }, { val: 4 }, { val: 5 }]

Temel kuralım map, kaynak listenin her bir öğesi için döndürmek üzere her zaman yeni bir nesne / değer oluşturduğunuzdan ve her öğe üzerinde bir işlem yapmak yerine onu döndürdüğünüzden emin olmaktır .

Mevcut listeyi değiştirmeye gerçekten ihtiyacınız olmadıkça, onu yerinde değiştirmek gerçekten mantıklı değildir ve işlevsel / değişmez programlama stillerine daha iyi uyum sağlar.


1

TL; DR yanıtı -

harita her zaman başka bir dizi döndürür.

forEach değil. Ne yapacağına karar vermek size kalmış. İsterseniz bir dizi döndürün veya istemiyorsanız başka bir şey yapın.

Belli durumlarda esneklik arzu edilir. Eğer uğraştığınız şey için değilse, haritayı kullanın.


0

Bir kez daha, 5 yıl önce sorulan bir soruya yanıt ekleyen bir büyücü gibi hissediyorum (ne mutlu ki, oldukça yeni bir aktivite var, bu yüzden ölüleri rahatsız eden tek kişi ben değilim :).

Diğerleri, işlevler arasındaki farkla ilgili ana sorunuz hakkında zaten yayınladılar. Ama için...

Javascript'te bu yöntemler için (bir veritabanını güncellemediğimiz için), bunun gibi sayıları değiştirmek dışında herhangi bir gerçek kullanım var mı:

... sorman komik. Daha bugün, dönüşüm için map kullanarak normal bir ifadeden birden çok değişkene bir dizi değer atayan bir kod parçası yazdım. Metin tabanlı çok karmaşık bir yapıyı görselleştirilebilir verilere dönüştürmek için kullanılıyordu ... ama basitlik uğruna, tarih dizelerini kullanarak bir örnek sunacağım, çünkü bunlar muhtemelen herkes için daha tanıdıktır (gerçi benim problemim aslında tarihlerle olsaydı , harita yerine, işi mükemmel bir şekilde kendi başına yapacak olan Tarih nesnesini kullanırdım).

const DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})Z$/;
const TEST_STRING = '2016-01-04T03:20:00.000Z';

var [
    iYear,
    iMonth,
    iDay,
    iHour,
    iMinute,
    iSecond,
    iMillisecond
    ] = DATE_REGEXP
        // We take our regular expression and...
        .exec(TEST_STRING)
        // ...execute it against our string (resulting in an array of matches)...
        .slice(1)
        // ...drop the 0th element from those (which is the "full string match")...
        .map(value => parseInt(value, 10));
        // ...and map the rest of the values to integers...

// ...which we now have as individual variables at our perusal
console.debug('RESULT =>', iYear, iMonth, iDay, iHour, iMinute, iSecond, iMillisecond);

Yani ... bu sadece bir örnek iken - ve sadece veriler için çok basit bir dönüşüm gerçekleştirirken (sadece örnek olarak) ... bunu harita olmadan yapmak çok daha sıkıcı bir iş olurdu.

Verilmiş, ben çok fazla tarayıcıların desteklediği sanmıyorum JavaScript sürümünde yazılır henüz (tam en azından) ama - biz orada alıyoruz. Tarayıcıda çalıştırmam gerekirse, güzelce aktarılacağına inanı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.