Самый быстрый способ конвертировать 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 за идею!
Предполагая 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):
Исходя из этих результатов, кажется, что метод 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'))