apa gunanya metode forEach javascript (peta itu tidak bisa melakukannya)?

91

Satu-satunya perbedaan yang saya lihat di map dan foreach mapadalah mengembalikan array dan forEachtidak. Namun, saya bahkan tidak mengerti baris terakhir dari forEachmetode " func.call(scope, this[i], i, this);". Misalnya, bukankah " this" dan " scope" merujuk ke objek yang sama dan bukan this[i]dan imerujuk ke nilai saat ini dalam pengulangan?

Saya melihat di posting lain seseorang berkata "Gunakan forEachsaat Anda ingin melakukan sesuatu berdasarkan setiap elemen daftar. Anda mungkin menambahkan sesuatu ke halaman, misalnya. Pada dasarnya, ini bagus untuk saat Anda menginginkan" efek samping ". Saya tidak tahu apa yang dimaksud dengan efek samping.

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

Akhirnya, apakah ada kegunaan nyata untuk metode ini dalam javascript (karena kami tidak memperbarui database) selain untuk memanipulasi angka seperti ini:

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

Terima kasih atas balasannya.

JohnMerlino
sumber
Bagaimana cara menemukan definisi fungsi dari JavaScript asli, seperti yang Anda lakukan dengan mapdan forEach? Yang saya dapatkan dari Google hanyalah spesifikasi penggunaan dan tutorialnya.
WoodrowShigeru
Lihat juga bahasa-agnostik Apakah ada perbedaan antara foreach dan peta?
Bergi

Jawaban:

95

Perbedaan penting antara mapdan forEachdalam contoh Anda adalah yang forEachberoperasi pada elemen larik asli, sedangkan mapsecara eksplisit mengembalikan larik baru sebagai hasilnya.

Dengan forEachAnda mengambil beberapa tindakan dengan - dan secara opsional mengubah - setiap elemen dalam larik asli. The forEachMetode menjalankan fungsi yang Anda berikan untuk setiap elemen, tetapi mengembalikan apa-apa ( undefined). Di sisi lain, mapmenelusuri array, menerapkan fungsi ke setiap elemen, dan memancarkan hasilnya sebagai array baru .

"Efek samping" dengan forEachadalah bahwa array asli sedang diubah. "Tanpa efek samping" mapartinya, dalam penggunaan idiomatik, elemen array asli tidak berubah; array baru adalah pemetaan satu-ke-satu dari setiap elemen dalam array asli - transformasi pemetaan menjadi fungsi yang Anda sediakan.

Fakta bahwa tidak ada database yang terlibat tidak berarti bahwa Anda tidak perlu beroperasi pada struktur data, yang bagaimanapun, adalah salah satu esensi pemrograman dalam bahasa apa pun. Adapun pertanyaan terakhir Anda, array Anda tidak hanya berisi angka, tetapi juga objek, string, fungsi, dll.

Ken Redler
sumber
4
catatan dari masa depan: jawaban ini sebenarnya tidak masuk akal; .mapdapat melakukan semua yang .forEachdapat dilakukan (termasuk memodifikasi elemen), ini hanya mengembalikan daftar baru yang dibuat dari fungsi iterator.
Travis Webb
6
Menanggapi dari masa lalu: Saya dengan hormat tidak setuju. .map()membuat larik baru, dan tidak mengubah aslinya. Anda memang dapat mengubah larik yang sedang dipetakan itu sendiri, tetapi itu setidaknya akan menjadi non-idiomatik, jika tidak masuk akal. .forEach(), meski serupa, menerapkan fungsinya ke setiap elemen, tetapi selalu mengembalikan undefined. Terlepas dari semua hal di atas, OP juga menanyakan tentang fungsi spesifiknya sendiri yang ditambahkan ke prototipe array; tidak ada ES5 di masa lalu.
Ken Redler
4
Masih ada hal yang forEachtidak dapat Anda lakukan map. Anda tidak peduli dengan nilai pengembalian dari mapdan Anda akan memiliki forEach. Perbedaannya adalah bahwa ini sangat tidak efektif digunakan mapuntuk tugas-tugas di mana Anda tidak ingin membuat larik baru berdasarkan hasil. Jadi untuk itu, Anda gunakan forEach.
aduk
2
@ Poke: ..dan juga, Anda dapat melakukan apa pun yang Anda butuhkan dengan forlingkaran lama yang sederhana . Saya tidak setuju bahwa ada beberapa perbedaan dalam "keefektifan", tetapi saya juga tidak berpikir ada orang yang berpendapat bahwa yang satu memiliki beberapa keajaiban yang entah bagaimana tidak dapat dicapai. Meskipun sebenarnya ada satu hal yang maptidak bisa dilakukan: tidak mengembalikan sebuah array. Ada nilai dalam memiliki JS memenuhi harapan dari bahasa lain untuk perilaku favorit fungsional seperti map, reduce, filter, dll Misalnya, Anda bisa menerapkan reduceRightmenggunakan blok bangunan serupa, tetapi mengapa tidak menggunakan reduceRight?
Ken Redler
1
@ChinotoVokro, contoh Anda secara eksplisit memutasikan array asli dengan menetapkan b.val = b.val**2 di dalam objek yang dikembalikan . Itulah tepatnya hal yang kami katakan itu mungkin tetapi membingungkan dan tidak idiomatis. Biasanya Anda hanya mengembalikan nilai baru, tidak juga menetapkannya ke array asli:a=[{val:1},{val:2},{val:3},{val:4}]; a.map((b)=>{b.val**2}); JSON.stringify(a);
Ken Redler
66

Perbedaan utama antara kedua metode tersebut adalah konseptual dan gaya: Anda gunakan forEachketika Anda ingin melakukan sesuatu pada atau dengan setiap elemen dari sebuah larik (menurut saya, melakukan "dengan" adalah apa yang dimaksud oleh pos yang Anda kutip dengan "efek samping") , sedangkan Anda menggunakan mapsaat Anda ingin menyalin dan mengubah setiap elemen array (tanpa mengubah aslinya).

Karena keduanya mapdan forEachmemanggil fungsi pada setiap item dalam larik, dan fungsi itu ditentukan pengguna, hampir tidak ada yang dapat Anda lakukan dengan satu dan tidak dengan yang lain. Mungkin saja, meskipun jelek, untuk digunakan mapuntuk memodifikasi array di tempat dan / atau melakukan sesuatu dengan elemen array:

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

tetapi jauh lebih bersih dan lebih jelas tentang niat Anda untuk menggunakan forEach:

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

Terutama jika, seperti yang biasanya terjadi di dunia nyata, eladalah variabel berguna yang dapat dibaca manusia:

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

Dengan cara yang sama, Anda dapat dengan mudah menggunakan forEachuntuk membuat array baru:

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

tapi lebih bersih untuk digunakan map:

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

Perhatikan juga bahwa, karena mapmembuat array baru, kemungkinan akan menimbulkan setidaknya beberapa kinerja / memori yang terpukul ketika yang Anda butuhkan hanyalah iterasi, terutama untuk array besar - lihat http://jsperf.com/map-foreach

Adapun mengapa Anda ingin menggunakan fungsi-fungsi ini, mereka sangat membantu setiap kali Anda perlu melakukan manipulasi array dalam javascript, yang (bahkan jika kita hanya berbicara tentang javascript di lingkungan browser) cukup sering, hampir setiap saat Anda mengakses larik yang tidak Anda tulis dengan tangan di kode Anda. Anda mungkin berurusan dengan larik elemen DOM di laman, atau data yang ditarik dari permintaan AJAX, atau data yang dimasukkan dalam formulir oleh pengguna. Salah satu contoh umum yang saya temui adalah menarik data dari API eksternal, di mana Anda mungkin ingin menggunakan mapuntuk mengubah data ke dalam format yang Anda inginkan dan kemudian menggunakannya forEachuntuk mengulangi array baru Anda untuk menampilkannya kepada pengguna Anda.

nrabinowitz
sumber
saya melihat orang menggunakan .map()sepanjang waktu untuk memodifikasi elemen sebaris. Saya mendapat kesan bahwa itu adalah manfaat utama menggunakan .mapover.forEach
chovy
1
@chovy Saya yakin Anda harus memilih berdasarkan apakah Anda ingin membuat serangkaian hasil. .mapdapat memodifikasi elemen, ya, tetapi itu akan membuat larik yang tidak Anda perlukan. Anda juga harus mempertimbangkan bagaimana memilih satu atau yang lain memungkinkan pembaca menebak apakah Anda memiliki efek samping atau tidak
leewz
16

Jawaban yang dipilih (dari Ken Redler) menyesatkan.

Sebuah efek samping dalam berarti ilmu komputer bahwa properti dari fungsi / metode alter sebuah negara global [wiki] . Dalam arti sempit, ini mungkin juga termasuk membaca dari keadaan global, bukan dari argumen. Dalam program imperatif atau OO, efek samping muncul hampir sepanjang waktu. Dan Anda mungkin memanfaatkannya tanpa menyadarinya.

Perbedaan signifikan antara forEachand mapis yang mapmengalokasikan memori dan menyimpan nilai yang dikembalikan, sementara forEachmembuangnya. Lihat spesifikasi emca untuk informasi lebih lanjut.

Adapun alasan mengapa orang mengatakan forEachdigunakan ketika Anda menginginkan efek samping adalah karena nilai pengembalian forEachselalu undefined. Jika tidak ada efek samping (tidak mengubah status global), maka fungsinya hanya membuang-buang waktu cpu. Kompilator yang mengoptimalkan akan menghilangkan blok kode ini dan menggantinya dengan nilai akhir ( undefined).

Ngomong-ngomong, perlu dicatat bahwa JavaScript tidak memiliki batasan pada efek samping. Anda masih dapat mengubah larik asli di dalamnya 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]
Jack Tang
sumber
2
Pertanyaan ini beserta jawaban dan komentarnya selalu menyenangkan untuk diputar kembali setiap beberapa tahun. Peta cara diimplementasikan di JS, dalam hal alokasi memori, adalah sudut bagus lainnya.
Ken Redler
7

Ini adalah pertanyaan yang indah dengan jawaban yang tidak terduga.

Berikut ini berdasarkan deskripsi resmi dariArray.prototype.map() .

Tidak ada yang forEach()bisa dilakukan yang map()tidak bisa. Artinya, map()adalah super-set yang ketat dari forEach().

Meskipun map()biasanya digunakan untuk membuat array baru, ini juga dapat digunakan untuk mengubah array saat ini. Contoh berikut menggambarkan hal ini:

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!!

Dalam contoh di atas, aitu mudah diatur sedemikian rupa sehingga a[i] === iuntuk i < a.length. Meski begitu, ia mendemonstrasikan kekuatan map(), dan khususnya kemampuannya untuk mengubah larik tempat ia dipanggil.

Note1:
Deskripsi resmi menyiratkan bahwa map()bahkan dapat mengubah panjang array yang dipanggil! Namun, saya tidak dapat melihat alasan (bagus) untuk melakukan ini.

Note2:
Meskipun map()peta adalah super-set forEach(), forEach()masih harus digunakan di mana seseorang menginginkan perubahan array yang diberikan. Ini membuat niat Anda jelas.

Semoga ini bisa membantu.

Sumukh Barve
sumber
8
Sebenarnya ada 1 hal yang forEach bisa lakukan yang peta tidak bisa lakukan - tidak mengembalikan array.
Antti Haapala
1
Anda juga dapat menggunakan argumen ketiga ke fungsi pemetaan untuk mengubah larik target, sebagai ganti variabel mapped = a.map(function (x, i, arr) { arr[i] = x * x * x; return x * x; });.
cakupan
@ pdoherty926 benar, tetapi bisa juga a.forEach(function(x, i, arr) { ..., yang, sekali lagi, merupakan cara yang lebih tepat secara konseptual ketika Anda bermaksud untuk mengubah setiap item asli sebagai lawan untuk memetakan nilai dari satu larik ke larik lainnya
conny
@conny: Setuju. Juga, silakan lihat jawaban ini , untuk pertanyaan serupa.
Sumukh Barve
3

Anda dapat menggunakannya mapseolah-olah forEach.

Namun, itu akan melakukan lebih dari yang seharusnya.

scopebisa menjadi objek sewenang-wenang; itu tidak berarti this.

Adapun apakah ada kegunaan nyata untuk mapdan forEach, juga untuk menanyakan apakah ada kegunaan nyata untuk foratau whileloop.

wombleton.dll
sumber
Tetapi mengapa "scope" dan "this" disebut di sini: func.call (scope, this [i], i, this); Bukankah lingkup parameter yang sama dengan objek saat ini, yaitu "ini"?
JohnMerlino
Tidak, itu bisa sama dengan objek saat ini. Objek itu sendiri diteruskan sebagai parameter ketiga ke array. scope = scope || thisberarti "jika scopefalsy (tidak ditentukan, null, false, dll) setel lingkup thissebagai gantinya dan lanjutkan".
wombleton
Dapatkah Anda menghubungkan saya ke sebuah contoh yang tidak sama dengan ini?
JohnMerlino
developer.mozilla.org/en/Core_JavaScript_1.5_Reference/… memiliki satu di bawah "Mencetak konten array dengan metode objek"
wombleton
1

Meskipun semua pertanyaan sebelumnya benar, saya pasti akan membuat perbedaan yang berbeda. Penggunaan mapdan forEachbisa menyiratkan niat.

Saya suka menggunakan mapketika saya hanya mengubah data yang ada dengan cara tertentu (tetapi ingin memastikan data asli tidak berubah.

Saya suka menggunakan forEachsaat saya memodifikasi koleksi di tempat.

Contohnya,

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

Aturan praktis saya adalah memastikan ketika Anda mapselalu membuat beberapa objek / nilai baru untuk dikembalikan untuk setiap elemen dari daftar sumber dan mengembalikannya daripada hanya melakukan beberapa operasi pada setiap elemen.

Kecuali jika Anda benar-benar perlu mengubah daftar yang ada, tidak masuk akal untuk memodifikasinya di tempat, dan lebih cocok dengan gaya pemrograman fungsional / tidak berubah.

maschwenk.dll
sumber
1

TL; DR jawaban -

map selalu mengembalikan larik lain.

forEach tidak. Terserah Anda untuk memutuskan apa yang dilakukannya. Kembalikan larik jika Anda ingin atau lakukan sesuatu yang lain jika tidak ingin.

Fleksibilitas diinginkan dalam situasi tertentu. Jika bukan karena apa yang Anda hadapi maka gunakan peta.

charsi
sumber
0

Sekali lagi, saya merasa seperti ahli nujum yang menambahkan jawaban atas pertanyaan yang telah diajukan 5 tahun yang lalu (untungnya ada aktivitas yang agak baru-baru ini, jadi saya bukan satu-satunya yang mengganggu orang mati :).

Orang lain telah memposting tentang pertanyaan utama Anda tentang perbedaan antara fungsi. Tapi untuk...

apakah ada kegunaan nyata untuk metode ini dalam javascript (karena kami tidak memperbarui database) selain untuk memanipulasi angka seperti ini:

... lucu kamu harus bertanya. Baru hari ini saya menulis sepotong kode yang menetapkan sejumlah nilai dari ekspresi reguler ke beberapa variabel menggunakan peta untuk transformasi. Itu digunakan untuk mengubah struktur berbasis teks yang sangat rumit menjadi data yang dapat divisualisasikan ... tetapi demi kesederhanaan, saya akan menawarkan contoh menggunakan string tanggal, karena itu mungkin lebih akrab bagi semua orang (meskipun, jika masalah saya sebenarnya terkait tanggal , alih-alih peta, saya akan menggunakan objek Tanggal, yang akan melakukan pekerjaannya sendiri dengan sangat baik).

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

Jadi ... sementara ini hanya sebuah contoh - dan hanya melakukan transformasi yang sangat mendasar untuk data (hanya sebagai contoh) ... melakukan ini tanpa peta akan menjadi tugas yang jauh lebih membosankan.

Memang, ada tertulis dalam versi JavaScript yang saya tidak berpikir terlalu banyak browser mendukung belum (setidaknya sepenuhnya) tapi - kita sudah ada. Jika saya perlu menjalankannya di browser, saya yakin ini akan berjalan dengan baik.

Markku Uttula
sumber