D3 javascript Perbedaan antara foreach dan each

89

Apa perbedaan antara forEachdan eachdi D3js?

Kuan
sumber

Jawaban:

179

Pertama, .forEach()ini bukan bagian dari d3, ini adalah fungsi asli dari array javascript. Begitu,

["a", "b", "c"].forEach(function(d, i) { console.log(d + " " + i); });
// Outputs:
a 0
b 1
c 2

Dan itu berfungsi bahkan jika d3 tidak dimuat di halaman.

Selanjutnya, d3 .each()bekerja pada pilihan d3 (apa yang Anda dapatkan saat Anda d3.selectAll(...)). Secara teknis, Anda dapat memanggil .forEach()pilihan d3, karena di belakang layar, pilihan d3 adalah larik dengan fungsi tambahan (salah satunya adalah .each()). Tetapi Anda tidak boleh melakukan itu karena:

  1. Melakukannya tidak akan menghasilkan perilaku yang diinginkan. Mengetahui cara menggunakan .forEach()dengan pilihan d3 untuk menghasilkan perilaku yang diinginkan akan membutuhkan pemahaman yang mendalam tentang cara kerja d3. Jadi mengapa melakukannya, jika Anda hanya dapat menggunakan bagian API yang terdokumentasi dan publik.

  2. Ketika Anda menelepon .each(function(d, i) { })pada pilihan d3, Anda mendapatkan lebih dari sekedar ddan i: fungsi akan dipanggil sehingga thisdi mana saja kata kunci dalam fungsi poin ke elemen HTML DOM terkait dengan d. Dengan kata lain console.log(this)dari dalam function(d,i) {}akan mencatat sesuatu seperti <div class="foo"></div>atau elemen html apa pun itu. Dan itu berguna, karena Anda dapat memanggil fungsi pada thisobjek ini untuk mengubah properti CSS, konten, atau apa pun. Biasanya, Anda menggunakan d3 untuk menyetel properti ini, seperti di d3.select(this).style('color', '#c33');.

Takeaway utama adalah bahwa, dengan menggunakan .each()Anda mendapatkan akses ke 3 hal yang perlu: d, thisdan i. Dengan .forEach(), pada sebuah array (seperti pada contoh dari awal) Anda hanya mendapatkan 2 hal ( ddan i), dan Anda harus melakukan banyak pekerjaan untuk juga mengaitkan elemen HTML dengan 2 hal tersebut. Dan itu antara lain adalah bagaimana d3 bermanfaat.

meetamit
sumber
17
Terima kasih telah menulis jawaban yang bagus, dan untuk melakukannya tanpa menyertakan snark yang tidak perlu yang begitu umum di SO ...
Kevin H. Lin
1
Harus ada peringatan di sini: ketika Anda memang membutuhkan pelingkupan berbeda untuk kata kunci 'ini' tetapi Anda tidak memerlukan datum dalam fungsi yang dipanggil, seleksi [0] .forEach (...) jauh lebih nyaman daripada seleksi. Masing-masing, yang memerlukan solusi 'self = this' dalam fungsi induk jika 'this' bermakna di luar referensi elemen DOM.
sdupton
@sdupton scoping for thisadalah masalah dalam banyak skenario d3 saat Anda meneruskan fungsi urutan yang lebih tinggi, termasuk misalnya selection.style("color", function(d,i) { /* here 'this' is a DOM element */ }). Saya percaya itu sebagian mengapa kelas d3 (seperti d3.svg.axismisalnya) tidak menggunakan prototypemetode mendefinisikan kelas - sebagai cara untuk menghindari ketergantungan this. Tapi saya tidak melihat bagaimana selection[0].forEach(...)menghindari masalah ini. Bukankah ini masalah yang sama?
meetamit
1
@meetamit Anda dapat secara eksplisit mencakup 'this' untuk digunakan di Array.prototype.forEach dengan argumen kedua, diteruskan setelah fungsi dipanggil pada setiap elemen. Saat Anda menulis sesuatu yang menyerupai pembungkus berorientasi objek (saya menggunakan kelas ES6), kehilangan pelingkupan eksplisit 'ini' bisa menyebalkan.
sdupton
2
@sdupton, keren - Saya tidak tahu .forEachmenerima param kedua untuk pelingkupan this. Itu membuat saya menyadari bahwa Anda dapat menggunakan sesuatu yang serupa untuk mencapai efek yang sama dengan d3 .each()dengan menggunakan .bind()metode javascript . Misalnya, kehendak lingkup berikut thisuntuk windowdan akan console.log itu: selection.each(function() { console.log(this); }.bind(window)).
meetamit