Cara paling efisien untuk mengonversi HTMLCollection ke Array

392

Apakah ada cara yang lebih efisien untuk mengubah HTMLCollection ke Array, selain iterasi melalui isi dari koleksi tersebut dan secara manual mendorong setiap item ke dalam array?

Tom
sumber
10
Apa yang dimaksud dengan "efisien"? Jika berkinerja terbaik, a for loop umumnya lebih cepat daripada Array.prototype.slice . Perulangan juga berfungsi di berbagai peramban yang lebih luas (yaitu semua), jadi dengan kriteria itu, ini adalah "cara paling efisien". Dan itu kode yang sangat sedikit: for (var a=[], i=collection.length; i;) a[--i] = collection[i];jadi tidak banyak "penipu" di sana :-)
RobG
@RobG Terima kasih - Saya akan memberi Anda + 59rb jika saya bisa! ;-)
Slashback
1
Melihat kinerja browser saat ini , sebagian besar irisan telah terjebak dengan loop dalam hal kinerja, kecuali di Chrome. Menggunakan sejumlah besar elemen dan sedikit optimisasi loop, hasilnya hampir identik , kecuali di Chrome di mana loop jauh lebih cepat.
RobG
Saya membuat tes jsperf yang melihat kedua metode yang disebutkan @harpo serta tes jquery untuk kinerja. Saya telah menemukan jquery sedikit lebih lambat daripada kedua metode javascript dan kinerja puncak bervariasi antara kasus uji js. Chrome 59.0.3071 / Mac OS X 10.12.5 lebih suka menggunakan Array.prototype.slice.calldan Brave (berdasarkan Chrome 59.0.3071) memiliki hampir tidak ada perbedaan antara dua tes javascript selama beberapa berjalan. Lihat jsperf.com/htmlcollection-array-vs-jquery-children
NuclearPeon
jsben.ch/h2IFA => tes kinerja untuk cara paling umum untuk melakukan ini
EscapeNetscape

Jawaban:

698
var arr = Array.prototype.slice.call( htmlCollection )

akan memiliki efek yang sama menggunakan kode "asli".

Edit

Karena ini mendapat banyak pandangan, perhatikan (per @ oriol's komentar) bahwa ungkapan yang lebih ringkas berikut ini secara efektif setara:

var arr = [].slice.call(htmlCollection);

Tetapi perhatikan per komentar JussiR, bahwa tidak seperti bentuk "verbose", itu membuat contoh array kosong, tidak digunakan, dan memang tidak dapat digunakan dalam proses. Apa yang dilakukan kompiler tentang hal ini adalah di luar ken programmer.

Edit

Sejak ECMAScript 2015 (ES 6) ada juga Array.from :

var arr = Array.from(htmlCollection);

Edit

ECMAScript 2015 juga menyediakan operator spread , yang secara fungsional setara dengan Array.from(meskipun catatan yang Array.frommendukung fungsi pemetaan sebagai argumen kedua).

var arr = [...htmlCollection];

Saya telah mengkonfirmasi bahwa kedua hal di atas berfungsi NodeList.

Perbandingan kinerja untuk metode yang disebutkan: http://jsben.ch/h2IFA

harpo
sumber
7
Ini gagal di IE6.
Heath Borders
29
Pintasan [].slice.call(htmlCollection)juga berfungsi.
Oriol
1
@ ChrisNielsen Ya saya salah informasi tentang itu. Maaf sudah menyebarkannya. Saya tidak menyadari bahwa saya telah menyatakannya di sini juga. Menghapus komentar untuk menghindari kebingungan tetapi untuk konteks saya telah membaca (atau salah membaca) di suatu tempat yang memotong HTMLCollection membuatnya berperilaku seperti array dan koleksi. Benar-benar salah.
Erik Reppen
3
Pintasan [] .slice tidak sama karena ia juga membuat instance array kosong yang tidak digunakan. Namun, tidak yakin apakah kompiler dapat mengoptimalkannya.
JussiR
3
Array.from, yaitu from, tidak didukung oleh IE11.
Frank Conijn
86

tidak yakin apakah ini yang paling efisien, tetapi sintaksis ES6 singkatnya mungkin:

let arry = [...htmlCollection] 

Sunting: Yang lain, dari komentar Chris_F:

let arry = Array.from(htmlCollection)
mido
sumber
9
Selain itu, ES6 menambahkanArray.from()
Chris_F
4
Hati-hati dengan yang pertama, ada bug halus ketika mentransformasikan dengan babel di mana [... htmlCollection] akan mengembalikan array dengan htmlCollection karena hanya elemen.
Marcel M.
3
Operator spread array tidak berfungsi di htmlCollection. Ini hanya berlaku untuk NodeList.
Bobby
1
Array.from, yaitu from, tidak didukung oleh IE11.
Frank Conijn
Benchmark Sepertinya operator spread lebih cepat dari 2 ini.
RedSparr0w
20

Saya melihat metode yang lebih ringkas untuk mendapatkan Array.prototypemetode secara umum yang juga berfungsi. Mengubah HTMLCollectionobjek menjadi Arrayobjek ditunjukkan di bawah ini:

[] .slice.call (yourHTMLCollectionObject);

Dan, seperti disebutkan dalam komentar, untuk browser lama seperti IE7 dan sebelumnya, Anda hanya perlu menggunakan fungsi kompatibilitas, seperti:

function toArray(x) {
    for(var i = 0, a = []; i < x.length; i++)
        a.push(x[i]);

    return a
}

Saya tahu ini adalah pertanyaan lama, tetapi saya merasa jawaban yang diterima sedikit tidak lengkap; jadi saya pikir saya akan membuang ini di sana FWIW.

Pembuat kode
sumber
6

Untuk implementasi lintas browser saya sarankan Anda melihat fungsi prototype.js $A

disalin dari 1.6.1 :

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

Mungkin tidak digunakan Array.prototype.slicekarena tidak tersedia di setiap browser. Saya khawatir kinerjanya sangat buruk karena ada kembali adalah javascript loop di atas iterable.

Gareth Davis
sumber
2
OP meminta cara lain selain "mengulangi isi dari koleksi tersebut dan secara manual mendorong setiap item ke dalam array", tetapi justru itulah $Afungsi yang paling sering dilakukan.
Luc125
1
Saya pikir poin yang saya coba buat adalah bahwa tidak ada cara yang baik untuk melakukannya, kode prototype.js menunjukkan bahwa Anda dapat mencari metode 'toArray' tetapi gagal iterasi rute teraman
Gareth Davis
1
Ini akan membuat anggota baru yang tidak terdefinisi dalam array jarang. Harus ada tes hasOwnProperty sebelum penugasan.
RobG
3

Ini adalah solusi pribadi saya, berdasarkan informasi di sini (utas ini):

var Divs = new Array();    
var Elemns = document.getElementsByClassName("divisao");
    try {
        Divs = Elemns.prototype.slice.call(Elemns);
    } catch(e) {
        Divs = $A(Elemns);
    }

Di mana $ A dijelaskan oleh Gareth Davis dalam jabatannya:

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

Jika browser mendukung cara terbaik, ok, kalau tidak akan menggunakan browser lintas.

Gustavo
sumber
Secara umum, saya tidak berharap mencoba / menangkap menjadi cara yang efisien untuk mengelola aliran kontrol. Anda dapat memeriksa apakah fungsi tersebut ada terlebih dahulu, kemudian jalankan salah satu atau yang lain sedikit lebih murah.
Patrick
2
Seperti dengan jawaban Gareth Davis', ini menciptakan baru, anggota terdefinisi dalam array jarang, sehingga [,,]menjadi [undefined, undefined].
RobG
Saya belum mendapatkan masalah seperti ini. Lapisan ini mengumpulkan 3 elemen hasil dalam array dengan 2 elemen. Sedangkan untuk kosong menjadi tidak terdefinisi, ini sedikit batasan JavaScript, saya kira Anda mengharapkan nol, bukan terdefinisi, kan?
Gustavo
3

Ini berfungsi di semua browser termasuk versi IE sebelumnya.

var arr = [];
[].push.apply(arr, htmlCollection);

Karena jsperf masih turun saat ini, berikut adalah jsfiddle yang membandingkan kinerja berbagai metode. https://jsfiddle.net/qw9qf48j/

Nicholas
sumber
cobavar args = (htmlCollection.length === 1 ? [htmlCollection[0]] : Array.apply(null, htmlCollection));
Shahar Shokrani
3

Untuk mengonversi array-suka menjadi array dengan cara yang efisien, kita dapat menggunakan jQuery makeArray :

makeArray: Konversi objek mirip array menjadi array JavaScript yang benar.

Pemakaian:

var domArray = jQuery.makeArray(htmlCollection);

Sedikit tambahan:

Jika Anda tidak ingin tetap merujuk ke objek array (sebagian besar waktu HTMLCollections berubah secara dinamis sehingga lebih baik untuk menyalinnya ke array lain, Contoh ini memperhatikan kinerja:

var domDataLength = domData.length //Better performance, no need to calculate every iteration the domArray length
var resultArray = new Array(domDataLength) // Since we know the length its improves the performance to declare the result array from the beginning.

for (var i = 0 ; i < domDataLength ; i++) {
    resultArray[i] = domArray[i]; //Since we already declared the resultArray we can not make use of the more expensive push method.
}

Seperti apa array itu?

HTMLCollection adalah "array-like"objek, array seperti benda mirip dengan objek array, tetapi kehilangan banyak definisi fungsional nya:

Objek seperti array terlihat seperti array. Mereka memiliki berbagai elemen bernomor dan properti panjang. Tapi di situlah kesamaan berhenti. Objek seperti array tidak memiliki fungsi Array, dan loop for-in bahkan tidak berfungsi!

Shahar Shokrani
sumber