HTMLCollection'ı Diziye dönüştürmenin en etkili yolu


391

Bir HTMLCollection'ı bir Diziye dönüştürmenin, adı geçen koleksiyonun içeriğini yinelemekten ve her öğeyi manuel olarak bir diziye itmekten daha etkili bir yolu var mı?


10
"Verimli" ile kastedilen nedir? En iyi performansı gösterirse, bir for döngüsü genellikle Array.prototype.slice öğesinden daha hızlıdır . Bir düğüm ayrıca böylece bu kriterlere göre bu tarayıcılar (yani tüm) daha geniş bir çalışan bir "en etkili bir şekilde". Ve bu çok küçük bir kod: for (var a=[], i=collection.length; i;) a[--i] = collection[i];çok fazla "con" orada :-)
RobG

@RobG Teşekkür ederim - yapabilseydim sana + 59k verirdim! ;-)
Slashback

1
Mevcut tarayıcı performansına bakıldığında , dilim Chrome hariç, performans açısından çoğunlukla döngülerle yakalandı. Daha fazla sayıda öğe ve döngünün hafif optimizasyonu kullanılarak, bir döngünün çok daha hızlı olduğu Chrome hariç , sonuçlar hemen hemen aynıdır .
RobG

@Harpo'nun bahsettiği her iki yönteme ve performans için bir jquery testine bakan bir jsperf testi oluşturdum. Jquery hem javascript yöntemlerinden biraz daha yavaş olduğunu gördüm ve en iyi performans js test vakaları arasında değişir. Chrome 59.0.3071 / Mac OS X 10.12.5 Array.prototype.slice.callve Brave kullanmayı tercih ediyor (Chrome 59.0.3071 tabanlı), birden fazla çalıştırmada iki javascript testi arasında neredeyse hiçbir fark yok. Bkz. Jsperf.com/htmlcollection-array-vs-jquery-children
NuclearPeon

jsben.ch/h2IFA => Bunu yapmanın en yaygın yolları için performans testi
EscapeNetscape

Yanıtlar:


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

"yerel" kod kullanılarak aynı etkiye sahip olacaktır.

Düzenle

Bu çok fazla görüş aldığından, aşağıdaki daha özlü ifadenin etkili bir şekilde eşdeğer olduğunu (@ oriol'un yorumuna göre) not edin :

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

Ancak @ JussiR'nin yorumuna göre, "ayrıntılı" formdan farklı olarak, işlemde boş, kullanılmayan ve gerçekten de kullanılamaz bir dizi örneği oluşturduğunu unutmayın. Derleyicilerin bu konuda yaptıkları programcının ken dışında.

Düzenle

ECMAScript 2015'ten (ES 6) beri Array.from var :

var arr = Array.from(htmlCollection);

Düzenle

ECMAScript 2015 ayrıca işlevsel olarak eşdeğer olan forma operatörünü de sağlar Array.from(her ne Array.fromkadar ikinci argüman olarak bir eşleme işlevini destekliyor olsa da ).

var arr = [...htmlCollection];

Yukarıdakilerin her ikisinin de çalıştığını doğruladım NodeList.

Bahsedilen yöntemler için bir performans karşılaştırması: http://jsben.ch/h2IFA


7
Bu IE6 başarısız.
Heath Borders

29
Kısayol [].slice.call(htmlCollection)da çalışır.
Oriol

1
@ChrisNielsen Evet, bu konuda yanlış bilgilendirildim. Bunu yaydığım için üzgünüm. Burada da ifade ettiğimin farkında değildim. Karışıklığı önlemek için yorumu sildim, ancak bağlam için bir HTMLCollection dilimlemenin hem bir dizi hem de bir koleksiyon gibi davrandığını bir yerde okudum (veya yanlış okudum). Tamamen yanlış.
Erik Reppen

3
Kullanılmayan boş dizi örneği de oluşturduğundan [] .slice kısayolu eşdeğer değildir. Derleyicilerin onu optimize edip edemeyeceğinden emin değilim.
JussiR

3
Array.fromyani fromIE11 tarafından desteklenmez.
Frank Conijn

86

bunun en verimli olup olmadığından emin değilim, ancak kısa bir ES6 sözdizimi şunlar olabilir:

let arry = [...htmlCollection] 

Edit: Başka bir, Chris_F yorumdan:

let arry = Array.from(htmlCollection)

9
Ayrıca, ES6 eklerArray.from()
Chris_F

4
Birincisine dikkat edin, babel ile aktarırken ince bir hata var; burada [... htmlCollection], htmlCollection ile bir dizi olan tek öğe olarak dönecektir.
Marcel

3
Dizi yayma işleci htmlCollection üzerinde çalışmaz. Yalnızca NodeList için geçerlidir.
Bobby

1
Array.fromyani fromIE11 tarafından desteklenmez.
Frank Conijn

Benchmark Spread operatörü bunlardan 2 daha hızlı görünüyor.
RedSparr0w

20

Array.prototypeGenel olarak yöntemlerin işe yaraması için daha özlü bir yöntem gördüm . Bir HTMLCollectionnesneyi bir nesneye dönüştürmeArray aşağıda gösterilmiştir:

[] .slice.call (yourHTMLCollectionObject);

Yorumlarda belirtildiği gibi, IE7 ve öncesi gibi eski tarayıcılar için , aşağıdaki gibi bir uyumluluk işlevi kullanmanız yeterlidir:

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

    return a
}

Bunun eski bir soru olduğunu biliyorum, ama kabul edilen cevabın biraz eksik olduğunu hissettim; bu yüzden bunu oraya atacağımı düşündüm FWIW.


6

Çapraz tarayıcı uygulaması için prototype.js $A işlevine bakmanızı öneririm

1.6.1'den kopyalandı :

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

Array.prototype.sliceHer tarayıcıda bulunmadığı için muhtemelen kullanılmaz. Korkarım orada bir düşme üzerinde bir javascript döngü olduğu gibi performansı oldukça kötü iterable.


2
OP, "söz konusu koleksiyonun içeriğini yinelemek ve her öğeyi bir diziye manuel olarak itmek" ten başka bir yol istedi, ancak $Aişlevin çoğu zaman yaptığı şey tam olarak bu.
Luc125

1
Yapmaya çalıştığım nokta, bunu yapmanın güzel bir yolu olmadığını düşünüyorum, prototype.js kodu bir 'toArray' yöntemi arayabileceğinizi ancak yinelemenin en güvenli yoldan başarısız olduğunu gösteriyor
Gareth Davis

1
Bu seyrek dizilerde yeni, tanımsız üyeler yaratacaktır. Atamadan önce bir hasOwnProperty testi olmalıdır .
RobG

3

Bu, buradaki bilgilere dayanarak kişisel çözümüm (bu konu):

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

$ A, Gareth Davis tarafından görevinde belirtildi:

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

Tarayıcı en iyi yolu destekliyorsa, tamam, aksi takdirde çapraz tarayıcıyı kullanır.


Genel olarak, denemenin / yakalamanın kontrol akışını yönetmek için etkili bir yol olmasını beklemiyorum. Önce işlevin var olup olmadığını kontrol edebilir, sonra birini veya diğerini biraz daha ucuz çalıştırabilirsiniz.
Patrick

2
Gareth Davis'in cevabında olduğu gibi, bu seyrek dizilerde yeni, tanımsız üyeler yaratır, öyle [,,]olur [undefined, undefined].
RobG

Henüz bu tür bir sorunla karşılaşmadım. 3 elemanlı bir toplama sonucu 2 elemanlı bir diziye ekler. Boş tanımsız hale gelince, biraz JavaScript sınırlamaları var, galiba tanımsız yerine null bekliyordunuz, değil mi?
Gustavo

3

Bu, önceki IE sürümleri de dahil olmak üzere tüm tarayıcılarda çalışır.

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

Jsperf şu anda hala kapalı olduğundan, burada farklı yöntemlerin performansını karşılaştıran bir jsfiddle var. https://jsfiddle.net/qw9qf48j/


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

3

Dizi benzeri diziyi verimli bir şekilde diziye dönüştürmek için jQuery'den yararlanabiliriz makeArray :

makeArray: Dizi benzeri bir nesneyi gerçek bir JavaScript dizisine dönüştürür.

Kullanımı:

var domArray = jQuery.makeArray(htmlCollection);

Biraz ekstra:

Dizi nesnesine başvurmak istemiyorsanız (çoğu zaman HTMLCollections dinamik olarak değişir, bu nedenle bunları başka bir diziye kopyalamak daha iyidir, Bu örnek performansa dikkat eder:

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

Dizi benzeri nedir?

HTMLCollection bir "array-like"nesnedir, dizi benzeri nesneler dizinin nesnesine benzer, ancak işlevsel tanımının çoğunu yoktur:

Dizi benzeri nesneler dizilere benzer. Çeşitli numaralandırılmış elemanlara ve length özelliğine sahiptirler. Ama benzerlik burada durur. Dizi benzeri nesneler, Dizinin işlevlerinden hiçbirine sahip değildir ve for-in döngüleri bile çalışmaz!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.