Самый быстрый способ конвертировать JavaScript NodeList в массив?

Ранее ответные вопросы здесь говорили, что это был самый быстрый способ:

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

В бенчмаркинге в моем браузере я обнаружил, что он более чем в 3 раза медленнее этого:

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

Оба они производят один и тот же результат, но мне трудно поверить, что моя вторая версия является самым быстрым способом, тем более, что люди говорили здесь иначе.

Является ли это причудой в моем браузере (Chromium 6)? Или есть более быстрый способ?

EDIT: для всех, кто волнуется, я остановился на следующем (что кажется самым быстрым в каждом браузере, который я тестировал):

//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: я нашел еще более быстрый способ

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

Ответ 1

В некоторых браузерах второй, как правило, работает быстрее, но главное, что вы должны использовать его, потому что первый из них просто не перекрестный браузер. Несмотря на то, что The Times They Are a-Changin '

@kangax (Предварительный просмотр IE 9)

Array.prototype.slice теперь может конвертировать определенные объекты хоста (например, NodeLists) к массивам - то, что большинство современные браузеры смогли сделать довольно долгое время.

Пример:

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

Ответ 2

С ES6 у нас теперь есть простой способ создания массива из NodeList: Array.from().

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

Ответ 4

Некоторые оптимизации:

  • сохранить длину NodeList в переменной
  • явно задает длину нового массива перед настройкой.
  • доступ к индексам, а не нажатие или неперемещение.

Код (jsPerf):

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

Ответ 5

Результаты полностью зависят от браузера, чтобы дать объективный вердикт, мы должны выполнить некоторые тесты производительности, вот некоторые результаты, вы можете запустить их здесь:

Chrome 6:

sec%20%28Chrome%206.0.453.1%20on%20Windows%20NT%29&chts=000000,10&cht=bhg&chd=t:3632,3994,3652&chds=0,3994&chxt=x&chxl=0:|0|4K&chsp=0,1&chm=tArray.prototype.slice.call%283.6K%29,000000,0,0,10|tfor%20loop%284K%29,000000,0,1,10|treverse%20while%20loop%283.7K%29,000000,0,2,10&chbh=15,0,5&chs=250x130

Firefox 3.6:

sec%20%28Firefox%203.6.3%20on%20Windows%20NT%29&chts=000000,10&cht=bhg&chd=t:1037,1333,1065&chds=0,1333&chxt=x&chxl=0:|0|1.3K&chsp=0,1&chm=tArray.prototype.slice.call%281K%29,000000,0,0,10|tfor%20loop%281.3K%29,000000,0,1,10|treverse%20while%20loop%281.1K%29,000000,0,2,10&chbh=15,0,5&chs=250x130

Firefox 4.0b2:

sec%20%28unknown%20platform%29&chts=000000,10&cht=bhg&chd=t:1349,2278,1096&chds=0,2278&chxt=x&chxl=0:|0|2.3K&chsp=0,1&chm=tArray.prototype.slice.call%281.3K%29,000000,0,0,10|tfor%20loop%282.3K%29,000000,0,1,10|treverse%20while%20loop%281.1K%29,000000,0,2,10&chbh=15,0,5&chs=250x130

Safari 5:

sec%20%28Safari%205.0%20on%20Windows%20NT%29&chts=000000,10&cht=bhg&chd=t:4881,3418,4281&chds=0,4881&chxt=x&chxl=0:|0|4.9K&chsp=0,1&chm=tArray.prototype.slice.call%284.9K%29,000000,0,0,10|tfor%20loop%283.4K%29,000000,0,1,10|treverse%20while%20loop%284.3K%29,000000,0,2,10&chbh=15,0,5&chs=250x130

Предварительный просмотр платформы IE9 3:

sec%20%28MSIE%209.0%20on%20Windows%20NT%29&chts=000000,10&cht=bhg&chd=t:373,304,379&chds=0,379&chxt=x&chxl=0:|0|379&chsp=0,1&chm=tArray.prototype.slice.call%28373%29,000000,0,0,10|tfor%20loop%28304%29,000000,0,1,10|treverse%20while%20loop%28379%29,000000,0,2,10&chbh=15,0,5&chs=250x130

Ответ 6

Самый быстрый и кросс-браузер

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

Как я сравнил в

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

* Спасибо @CMS за идею!

Chromium (Similar to Google Chrome)FirefoxOpera

Ответ 7

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

Теперь вы можете сделать document.querySelectorAll('div'). forEach (function()...)

Ответ 8

быстрее и короче:

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

Ответ 9

Просмотрите этот пост в блоге здесь, в котором говорится о том же. Из того, что я собираю, дополнительное время, возможно, связано с хождением по цепочке областей.

Ответ 10

Это функция, которую я использую в своем JS:

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

Ответ 11

В ES6 вы можете использовать:

  • Array.from

    let array = Array.from(nodelist)

  • Оператор спреда

    let array = [...nodelist]

Ответ 12

Вот графики, обновленные на дату публикации (диаграмма "неизвестной платформы" - Internet Explorer 11.15.16299.0):

Safari 11.1.2Firefox 61.0Chrome 68.0.3440.75Internet Explorer 11.15.16299.0

Из этих результатов кажется, что метод preallocate 1 является самой безопасной кросс-браузерной ставкой.

Ответ 13

Простой способ конвертировать массивоподобные объекты в массив

nodeList = Array.from(document.querySelectorAll('li'))
// Checking if it array
Array.isArray(nodeList) // true

Ответ 14

Просто будучи завершающим здесь, и это быстро, чтобы напечатать:

let arr1 = Array.prototype.filter.call(nl, n => true);

let arr2 = Array.prototype.map.call(nl, n => n);

Ответ 15

let myArray = Array.from(nodelist)

Ответ 16

Предполагая nodeList = document.querySelectorAll("div"), это краткая форма преобразования nodelist в массив.

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

Посмотрите, как я это использую здесь.