JavaScript - Nuansa myArray.forEach vs for loop

88

Saya telah melihat banyak pertanyaan yang menyarankan penggunaan:

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

dari pada:

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

untuk array, karena iterasi yang tidak konsisten ( lihat di sini ).


Namun, saya tidak dapat menemukan apa pun yang tampaknya lebih menyukai loop berorientasi objek:

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

Yang tampaknya jauh lebih intuitif bagi saya.

Untuk proyek saya saat ini, kompatibilitas IE8 adalah penting, dan saya mempertimbangkan untuk menggunakan polyfill Mozilla , namun saya tidak 100% yakin bagaimana ini akan bekerja.

  • Apakah ada perbedaan antara standar for loop (contoh pertama di atas) dan implementasi Array.prototype.forEach oleh browser modern?
  • Apakah ada perbedaan antara implementasi browser modern dan implementasi Mozilla yang ditautkan di atas (dengan perhatian khusus pada IE8)?
  • Performa bukanlah masalah yang utama, hanya konsistensi properti yang diiterasi.
Michael Lewis
sumber
6
Tidak mungkin breakkeluar forEach. Tetapi keuntungan besar adalah membuat ruang lingkup baru dengan fungsi tersebut. Dengan polyfill Anda seharusnya tidak mengalami masalah (setidaknya saya belum menemui masalah).
hgoebl
Masalah yang dapat Anda miliki dengan IE yang lebih lama, bukanlah shim itu sendiri, tetapi konstruktor array yang rusak / wrt literal di holesmana undefinedseharusnya dan metode rusak lainnya, seperti slicedan hasOwnPropertywrt ke objek DOM seperti array. Pengujian saya dan es5 shimtelah menunjukkan metode shim yang sesuai dengan spesifikasi (tidak diuji shim MDN).
Xotic750
1
Dan berusaha untuk keluar dari forlingkaran, untuk itulah some.
Xotic750
1
"Namun, saya tidak dapat menemukan apa pun yang tampaknya lebih menyukai loop berorientasi objek:" Saya lebih suka menyebutnya cara fungsional vs imperatif.
Memke
Anda dapat menggunakan Array.find()untuk keluar dari loop setelah menemukan kecocokan pertama.
Mottie

Jawaban:

122

Perbedaan paling mendasar antara forloop dan forEachmetode adalah, dengan metode sebelumnya, Anda mungkin breakkeluar dari loop. Anda dapat melakukan simulasi continuehanya dengan kembali dari fungsi yang diteruskan ke forEach, tetapi tidak ada cara untuk menghentikan perulangan sama sekali.

Selain itu, keduanya mencapai fungsi yang sama secara efektif. Perbedaan kecil lainnya melibatkan cakupan indeks (dan semua variabel yang mengandung) di loop for, karena pengangkatan variabel.

// '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) { ... });

Namun, menurut saya itu forEachjauh lebih ekspresif — ini mewakili niat Anda untuk mengulang melalui setiap elemen array, dan ini memberi Anda referensi ke elemen, bukan hanya indeks. Secara keseluruhan, sebagian besar tergantung pada selera pribadi, tetapi jika Anda dapat menggunakannya forEach, saya akan merekomendasikan untuk menggunakannya.


Ada beberapa perbedaan substansial antara kedua versi tersebut, khususnya terkait kinerja. Nyatanya, loop for yang sederhana bekerja jauh lebih baik daripada forEachmetode, seperti yang ditunjukkan oleh pengujian jsperf ini .

Apakah penampilan seperti itu diperlukan bagi Anda atau tidak, terserah Anda untuk memutuskan, dan dalam banyak kasus, saya lebih menyukai ekspresi daripada kecepatan. Perbedaan kecepatan ini kemungkinan besar disebabkan oleh perbedaan semantik kecil antara loop dasar dan metode saat beroperasi pada array renggang, seperti yang dijelaskan dalam jawaban ini .

Jika Anda tidak membutuhkan perilaku forEachdan / atau Anda perlu keluar dari loop lebih awal, Anda dapat menggunakan Lo-Dash _.eachsebagai alternatif, yang juga akan bekerja lintas browser. Jika Anda menggunakan jQuery, ini juga menyediakan yang serupa $.each, cukup perhatikan perbedaan dalam argumen yang diteruskan ke fungsi callback di setiap variasi.

(Sedangkan untuk forEachpolyfill, ini harus bekerja di browser lama tanpa masalah, jika Anda memilih untuk pergi ke rute itu.)

Alexis King
sumber
1
Perlu dicatat bahwa versi perpustakaan eachtidak berperilaku dengan cara yang sama seperti spesifikasi ECMA5 forEach, mereka cenderung memperlakukan semua larik sebagai padat (untuk menghindari bug IE, asalkan Anda mengetahuinya). Bisa menjadi "gotcha" sebaliknya. Sebagai referensi github.com/es-shims/es5-shim/issues/190
Xotic750
7
Juga ada beberapa metode prototipe yang memungkinkan Anda untuk istirahat seperti Array.prototype.someyang akan mengulang sampai Anda mengembalikan nilai yang benar atau sampai telah diulang sepenuhnya melalui array. Array.prototype.everymirip dengan Array.prototype.sometetapi berhenti jika Anda mengembalikan nilai yang salah.
axelduch
Jika Anda ingin melihat beberapa hasil tes pada browser yang berbeda dan shims tersebut, Anda dapat melihatnya di sini, ci.testling.com/Xotic750/util-x
Xotic750
Selain itu, menggunakan Array.prototype.forEach akan membuat kode Anda bergantung pada ekstensi. Misalnya, jika Anda menulis kode untuk disematkan ke halaman, ada kemungkinan Array.prototype.forEach ditimpa dengan sesuatu yang lain.
Marble Daemon
2
@MarbleDaemon Kemungkinannya sangat kecil sehingga secara efektif tidak mungkin dan karena itu dapat diabaikan. Menimpa Array.prototype.forEachdengan beberapa versi yang tidak kompatibel akan berpotensi merusak begitu banyak pustaka sehingga menghindarinya karena alasan itu tidak akan membantu.
Alexis King
12

Anda dapat menggunakan fungsi foreach kustom Anda yang akan bekerja jauh lebih baik daripada Array.forEach

Anda harus menambahkan ini sekali ke kode Anda. Ini akan menambah fungsi baru ke Array.

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

Kemudian Anda dapat menggunakannya di mana saja seperti Array.forEach

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

});

Satu-satunya perbedaan adalah 3 kali lebih cepat. https://jsperf.com/native-arr-foreach-vs-custom-foreach

PEMBARUAN: Di versi Chrome baru, kinerja .forEach () ditingkatkan. Namun, solusi tersebut dapat memberikan kinerja tambahan di browser lain.

JSPerf

Camar
sumber
4

Disarankan oleh banyak pengembang (misalnya Kyle Simpson) untuk digunakan .forEachuntuk menunjukkan bahwa larik akan memiliki efek samping dan .mapuntuk fungsi murni. forloop cocok sebagai solusi tujuan umum untuk jumlah loop yang diketahui atau kasus lain yang tidak sesuai karena lebih mudah untuk berkomunikasi karena dukungannya yang luas di sebagian besar bahasa pemrograman.

misalnya

/* 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()));

Dari segi konvensi, ini tampaknya yang terbaik, namun komunitas belum benar-benar menyetujui konvensi pada for inloop protokol iterasi yang lebih baru . Secara umum, menurut saya, mengikuti konsep KB yang tampaknya terbuka untuk diadopsi oleh komunitas JS merupakan ide yang bagus.

steviejay
sumber