JavaScript NodeList'i Array'a dönüştürmenin en hızlı yolu?


251

Burada daha önce cevaplanan sorular bunun en hızlı yol olduğunu söyledi:

//nl is a NodeList
var arr = Array.prototype.slice.call(nl);

Tarayıcımdaki karşılaştırmada, bundan 3 kat daha yavaş olduğunu gördüm:

var arr = [];
for(var i = 0, n; n = nl[i]; ++i) arr.push(n);

Her ikisi de aynı çıktıyı üretiyorlar, ancak ikinci sürümümün mümkün olan en hızlı yol olduğuna inanmakta zorlanıyorum, özellikle insanlar burada aksini söylediğinden.

Bu tarayıcımda bir sorun mu (Chromium 6)? Yoksa daha hızlı bir yol var mı?

EDIT: önemseyen herkes için aşağıdakilere karar verdim (test ettiğim her tarayıcıda en hızlı gibi görünüyor):

//nl is a NodeList
var l = []; // Will hold the array of Node's
for(var i = 0, ll = nl.length; i != ll; l.push(nl[i++]));

EDIT2: Daha da hızlı bir yol buldum

// nl is the nodelist
var arr = [];
for(var i = nl.length; i--; arr.unshift(nl[i]));

2
arr[arr.length] = nl[i];arr.push(nl[i]);işlev çağrısından kaçındığından daha hızlı olabilir .
Luc125

9
Bu jsPerf sayfası, bu sayfadaki tüm cevapları takip ediyor: jsperf.com/nodelist-to-array/27
pilau

IE8'de "EDIT2: Daha hızlı bir yol buldum" un% 92 daha yavaş olduğunu lütfen unutmayın.
Camilo Martin

2
Zaten bildiğimiz için kaç tane düğüm biliyoruz:var i = nl.length, arr = new Array(i); for(; i--; arr[i] = nl[i]);
mems

@ Luc125 Push uygulaması optimize edilebildiğinden tarayıcıya bağlı, v8 bu tür şeyler için iyi olduğu için krom hakkında düşünüyorum.
axelduch

Yanıtlar:


197

İkincisi bazı tarayıcılarda daha hızlı olma eğilimindedir, ancak asıl nokta onu kullanmanız gerektiğidir, çünkü ilki sadece tarayıcılar arası değildir. Değişen Zamanlar Olsa '

@kangax ( IE 9 önizleme )

Array.prototype.slice artık belirli ana bilgisayar nesnelerini (örneğin, NodeList'ler) dizilere dönüştürebilir - modern tarayıcıların çoğunun bir süredir yapabildiği bir şey.

Misal:

Array.prototype.slice.call(document.childNodes);

??? Her ikisi de çapraz tarayıcı uyumludur - Javascript (en azından ECMAscript spesifikasyonu ile uyumlu olduğunu iddia ederse) Javascript'tir; Dizi, prototip, dilim ve çağrı temel dil + nesne türlerinin özellikleridir.
Jason S

6
ama IE'de NodeLists üzerinde kullanılamazlar (berbat olduğunu biliyorum, ama hey güncellememe bakın)
gblazex


3
IE8'i hesaba katarsanız Array.prototype.slice çapraz tarayıcı değildir.
Lajos Meszaros

1
Evet, cevabım temel olarak bu oldu :) 2010'da (2015) olduğundan daha alakalı olmasına rağmen.
gblazex

224

ES6 ile artık bir NodeList öğesinden bir Array oluşturmanın basit bir yoluna sahibiz: Array.from()işlev.

// nl is a NodeList
let myArray = Array.from(nl)

Bu yeni ES6 yöntemi, yukarıda bahsedilen diğer hızlarla nasıl karşılaştırılır?
user5508297

10
@ user5508297 Dilim arama numarasından daha iyi. Döngülerden daha yavaştır, ancak bu tam olarak geçmeden bir diziye sahip olmak isteyebiliriz. Ve sözdizimi güzel, basit ve hatırlanması kolay!
webdif

Array.from hakkında güzel bir şey, harita argümanını kullanabilmenizdir:console.log(Array.from([1, 2, 3], x => x + x)); // expected output: Array [2, 4, 6]
honzajde


19

Bazı optimizasyonlar:

  • NodeList'in uzunluğunu bir değişkene kaydedin
  • ayarlamadan önce yeni dizinin uzunluğunu açıkça ayarlayın.
  • itme veya kaydırma yerine endekslere erişir.

Kodu ( jsPerf ):

var arr = [];
for (var i = 0, ref = arr.length = nl.length; i < ref; i++) {
 arr[i] = nl[i];
}

Diziyi oluşturmak ve sonra ayrı ayrı boyutlandırmak yerine Array (uzunluk) kullanarak biraz daha fazla tıraş olabilirsiniz . Daha sonra diziyi ve uzunluğunu döngü içinde bir izinle bildirmek için const kullanırsanız, yukarıdaki yöntemden yaklaşık% 1,5 daha hızlı sonuç verir: const a = Dizi (nl.length), c = a.length; for (let b = 0; b <c; b ++) {a [b] = nl [b]; } bkz. jsperf.com/nodelist-to-array/93
BrianFreud

15

Sonuçlar tamamen tarayıcıya bağlı olacak, nesnel bir karar vermek için bazı performans testleri yapmalıyız, işte bazı sonuçlar, burada çalıştırabilirsiniz :

Chrome 6:

Firefox 3.6:

Firefox 4.0b2:

Safari 5:

IE9 Platform Önizleme 3:


1
Döngünün tam tersi bunlara karşı nasıl duruyor ... for (var i=o.length; i--;)... bu testlerde 'for döngüsü' her yinelemede length özelliğini yeniden değerlendirdi mi?
Dagg Nabbit

14

En hızlı ve çapraz tarayıcı

for(var i=-1,l=nl.length;++i!==l;arr[i]=nl[i]);

Kıyasladığım gibi

http://jsbin.com/oqeda/98/edit

* Fikir için @CMS teşekkürler!

Chromium (Google Chrome'a ​​benzer) Firefox Opera


1
bağlantı yanlış görünüyor, bahsettiğiniz testi dahil etmek için 89 yerine 91 olmalıdır. Ve 98 en eksiksiz olanı gibi görünüyor.
Yaroslav Yakovlev

9

ES6'da şunları kullanabilirsiniz:

  • Array.from

    let array = Array.from(nodelist)

  • Forma operatörü

    let array = [...nodelist]


Önceki yanıtlarda zaten yazılanlara yeni bir şey eklemez.
Stefan Becker

7
NodeList.prototype.forEach = Array.prototype.forEach;

Artık document.querySelectorAll ('div'). ForEach (function () ...) yapabilirsiniz


İyi fikir, teşekkürler @John! Ancak, NodeListçalışmıyor ama Object: Object.prototype.forEach = Array.prototype.forEach; document.getElementsByTagName("img").forEach(function(img) { alert(img.src); });
Ian Campbell

3
Object.prototype kullanmayın: JQuery ve sözlük değişmez sözdizimi gibi bir sürü şeyi bozar.
Nate Symer

Elbette, yerleşik yerleşik işlevleri genişletmekten kaçının.
Roland

5

daha hızlı ve daha kısa:

// nl is the nodelist
var a=[], l=nl.length>>>0;
for( ; l--; a[l]=nl[l] );

3
Neden >>>0? Ve ödevleri neden for döngüsünün ilk kısmına koymuyorsunuz?
Camilo Martin

5
Ayrıca, bu adamcağız. Ne zaman lolduğunu 0, döngü nedenle sona erecek 0inci eleman kopyalanmayacaktır (hatırlıyorum endeksine bir unsur var 0)
Camilo Martin

1
Bu yanıtı seviyorum, ama ... Merak eden herkes: burada gerekli >>> olmayabilir , ancak nodelist uzunluğunun dizi spesifikasyonuna uyduğunu garanti etmek için kullanılır; işaretsiz bir 32 bit tam sayı olmasını sağlar. Buradan kontrol edin ecma-international.org/ecma-262/5.1/#sec-15.4 Okunamaz kodu beğeniyorsanız, @ CamiloMartin önerileriyle bu yöntemi kullanın!
Todd

@CamiloMartin adlı kişiye yanıt olarak - 'Değişken kaldırma' nedeniyle varbir forhalkanın ilk kısmının içine girmek risklidir . Satırı aşağıda bir yerde görünse varbile, bildirimi işlevin üst kısmına 'kaldırılır' varve bu, kod dizisinden belirgin olmayan yan etkilere neden olabilir. Örneğin aynı işlevi bazı kod oluşan önce döngü için olabilir bağlıdır üzerinde ave latanarak edilir. Bu nedenle, daha fazla öngörülebilirlik için, işlevin en üstündeki değişkenlerinizi (veya ES6'da, kaldırmayın constveya letyerine kullanın) bildirin .
brennanyoung

3

Bu blog yazısı kontrol edin burada aynı şeyi o konuşur. Topladığım kadarıyla, ekstra zamanın kapsam zincirini yukarı doğru yürümekle ilgili olması gerekebilir.


İlginç. Şu anda bazı benzer testler yaptım ve Firefox 3.6.3, her iki şekilde de hızda bir artış göstermezken, Opera 10.6'da% 20'lik bir artış ve Chrome 6'da manuel iterasyonlu itme yolunda% 230 (!) Artış var.
jairajs89

@ jairajs89 oldukça garip. Array.prototype.sliceTarayıcıya bağlı olduğu anlaşılıyor . Her tarayıcının hangi algoritmayı kullandığını merak ediyorum.
Vivin Paliath

3

Bu benim JS kullanmak işlevdir:

function toArray(nl) {
    for(var a=[], l=nl.length; l--; a[l]=nl[l]);
    return a;
}

1

İşte bu yayın tarihi itibariyle güncellenen grafikler ("bilinmeyen platform" şeması Internet Explorer 11.15.16299.0):

Safari 11.1.2 Firefox 61.0 Krom 68.0.3440.75 Internet Explorer 11.15.16299.0

Bu sonuçlardan, preallocate 1 yönteminin en güvenli tarayıcılar arası bahis olduğu görülmektedir.


1

Diyelim ki nodeList = document.querySelectorAll("div")bu, nodelistdiziye dönüştürmenin kısa bir biçimidir .

var nodeArray = [].slice.call(nodeList);

Bakın burada kullanın .

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.