Filter atau petakan nodelist di ES6

90

Apa cara paling efisien untuk memfilter atau memetakan nodelist di ES6?

Berdasarkan pembacaan saya, saya akan menggunakan salah satu opsi berikut:

[...nodelist].filter

atau

Array.from(nodelist).filter

Mana yang akan Anda rekomendasikan? Dan apakah ada cara yang lebih baik, misalnya tanpa melibatkan array?

Christophe
sumber
2
Pada dasarnya kedua metode tersebut melakukan hal yang sama. Jika Anda menggunakan babel, maka [...coll]hanya akan memanggil Array.from(coll)apa pun yang bukan file Array.
Leonid Beschastny
FWIW, ...sintaks mungkin tidak didukung oleh IDE yang lebih lama sementara Array.from()itu hanya metode biasa.
Marat Tanalin

Jawaban:

131
  • [...nodelist] akan membuat sebuah array dari sebuah objek jika objek tersebut dapat diulang.
  • Array.from(nodelist)akan membuat sebuah array dari sebuah objek jika objek iterable atau jika objek tersebut seperti array (has .lengthdan numeric props)

Kedua contoh Anda akan sama jika NodeList.prototype[Symbol.iterator]ada, karena kedua kasus mencakup iterable. Jika lingkungan Anda belum dikonfigurasi sedemikian rupa sehingga NodeListdapat diulang, contoh pertama Anda akan gagal, dan yang kedua akan berhasil. Babelsaat ini tidak menangani kasus ini dengan benar .

Jadi jika Anda NodeListdapat iterable, terserah Anda yang mana yang Anda gunakan. Saya kemungkinan besar akan memilih berdasarkan kasus per kasus. Salah satu manfaatnya Array.fromadalah dibutuhkan argumen kedua dari fungsi pemetaan, sedangkan yang pertama [...iterable].map(item => item)harus membuat array sementara, Array.from(iterable, item => item)tidak akan. Namun, jika Anda tidak memetakan daftar, itu tidak masalah.

loganfsmyth
sumber
17

TL; DR;

Array.prototype.slice.call(nodelist).filter

Metode slice () mengembalikan larik. Array yang dikembalikan itu adalah salinan dangkal dari koleksi (NodeList) Jadi ini bekerja lebih cepat daripada Array.from () Jadi itu bekerja secepat Array.from ()

Elemen dari koleksi asli disalin ke dalam larik yang dikembalikan sebagai berikut:

  • Untuk referensi objek (dan bukan objek sebenarnya), slice menyalin referensi objek ke dalam array baru. Baik array asli dan baru merujuk ke objek yang sama. Jika objek yang direferensikan berubah, perubahan tersebut terlihat oleh array baru dan asli.
  • Untuk string, angka, dan boolean (bukan objek String, Angka, dan Boolean), slice akan menyalin nilai ke dalam array baru. Perubahan pada string, angka atau boolean dalam satu larik tidak mempengaruhi larik lainnya.

Penjelasan singkat tentang argumen

Array.prototype.slice (beginIndex, endIndex)

  • mengambil args opsional beginIndex dan endIndex. Jika tidak tersedia, slice menggunakan beginIndex == 0, sehingga mengekstrak semua item dari koleksi

Array.prototype.slice.call (namespace, beginIndex, endIndex)

  • mengambil sebuah objek sebagai argumen pertama. Jika kita menggunakan koleksi sebagai objek, itu secara harfiah berarti bahwa kita memanggil metode slice langsung dari namespace objek itu.slice ()
Serge Seletskyy
sumber
2
Terima kasih atas cuplikan kode ini, yang mungkin memberikan bantuan terbatas dan langsung. Penjelasan yang tepat akan sangat meningkatkan nilai jangka panjangnya dengan menunjukkan mengapa ini adalah solusi yang baik untuk masalah tersebut dan akan membuatnya lebih berguna bagi pembaca di masa mendatang dengan pertanyaan serupa lainnya. Harap edit jawaban Anda untuk menambahkan penjelasan, termasuk asumsi yang Anda buat.
Maximilian Peters
Saya bertanya-tanya apakah ini memiliki dukungan IE karena Array.fromtidak. Saatnya mencari mesin IE. Sekarang saya benar-benar bingung karena saya bisa menggunakan Array.from di IE10 dan IE11: \. Metode ini berfungsi di IE10 + 11 tetapi saya tidak dimudahkan oleh Array.from bekerja ketika semua dokumentasi mengatakan sebaliknya.
CTS_AE
Array.fromtidak bekerja untuk saya di IE11 Objek tidak mendukung properti atau metode 'dari'
Fus Ro Dah
Terima kasih, ini berhasil untuk saya pada implementasi lama JavaScript
Vic Seedoubleyew
1
Array.fromjuga mengembalikan salinan dangkal. Jadi saya tidak melihat bagaimana Anda menyimpulkan bahwa itu bekerja lebih cepat dari Array#slice.
Robert
9

Saya menemukan referensi yang menggunakan maplangsung di NodeList oleh

Array.prototype.map.call(nodelist, fn)

Saya belum mengujinya, tetapi tampaknya masuk akal bahwa ini akan lebih cepat karena harus mengakses NodeList secara langsung.

goweon
sumber
4

Bagaimana dengan ini:

// Be evil. Extend the prototype.
if (window.NodeList && !NodeList.prototype.filter) {
  NodeList.prototype.filter = Array.prototype.filter;
}

// Use it like you'd expect:
const noClasses = document
  .querySelectorAll('div')
  .filter(div => div.classList.length === 0)

Ini pendekatan yang sama seperti yang disebutkan dalam dokumen MDN untuk NodeList.forEach (di bawah 'Polyfill'), ini bekerja untuk IE11 , Edge, Chrome dan FF.

panepeter
sumber
sedikit peringatan, sekarang nodeList.filter kemudian akan memberi Anda sebuah array, bukan daftar node. seharusnya tidak menjadi masalah, tetapi mudah untuk dilupakan ^^
hanshenrik