Самый быстрый способ конвертировать 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)? Или есть более быстрый способ?

РЕДАКТИРОВАТЬ: Для всех, кто заботится, я остановился на следующем (который, кажется, самый быстрый в каждом браузере, который я тестировал):

//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]));

18 ответов

Решение

Второй, как правило, работает быстрее в некоторых браузерах, но суть в том, что вы должны использовать его, потому что первый не является кросс-браузерным. Хотя времена они меняют

@kangax (IE 9 превью)

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

Пример:

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

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

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

Вот новый крутой способ сделать это с помощью оператора распространения ES6:

let arr = [...nl];

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

  • Array.from

    let array = Array.from(nodelist)

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

    let array = [...nodelist]

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

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

Код ( jsPerf):

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

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

Chrome 6:

Firefox 3.6:

Firefox 4.0b2:

Safari 5:

IE9 Platform Preview 3:

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

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

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

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

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

Chromium (аналог Google ChromeFire Foxопера

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

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

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

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

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

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

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

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

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

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

Здесь один лайнер, я не уверен, что он безопасен, но у меня он работает, он перезаписывает переменную нодлиста массивом, потому что я больше не использую нодлист, так как преобразовал его в массив. Я нахожу это решение более чистым, поскольку оно использует только одну переменную.

this.openButtons = [...this.openButtons]

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

Safari 11.1.2Firefox 61.0Хром 68.0.3440.75Internet Explorer 11.15.16299.0

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

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

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

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

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

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

Самый простой способ:

      Array.from(document.querySelectorAll('.back-top'))

просто сделайте это ндс arr = [...nodeList]

Другие вопросы по тегам