Найефективніший спосіб перетворення HTMLCollection в масив


391

Чи існує більш ефективний спосіб перетворення HTMLCollection в масив, окрім повторення вмісту згаданої колекції та вручну натискання кожного елемента в масив?


10
Що означає "ефективний"? Якщо це найкраще, цикл for for, як правило, швидше, ніж Array.prototype.slice . Цикл також працює в більш широкому розмаїтті браузерів (тобто всі), так що за цими критеріями вона є «найбільш ефективним способом». І це дуже мало коду: for (var a=[], i=collection.length; i;) a[--i] = collection[i];так що не так багато "кон" там :-)
RobG

@RobG Дякую - я дав би тобі 59к, якби міг! ;-)
Slashback

1
Дивлячись на поточну ефективність браузера , фрагмент здебільшого наздогнав циклі з точки зору продуктивності, за винятком Chrome. Використовуючи більшу кількість елементів і незначну оптимізацію циклу, результати майже однакові , за винятком Chrome, де цикл набагато швидший.
RobG

Я створив тест jsperf, який розглядає обидва методи, які згадував @harpo, а також тест jquery на продуктивність. Я виявив, що jquery трохи повільніше, ніж обидва способи javascript, а найкраща ефективність залежить від тестових випадків js. Chrome 59.0.3071 / Mac OS X 10.12.5 віддає перевагу використанню, Array.prototype.slice.callа Brave (заснований на Chrome 59.0.3071) практично не має різниці між двома тестами javascript у кількох запусках. Дивіться jsperf.com/htmlcollection-array-vs-jquery-children
NuclearPeon

jsben.ch/h2IFA => тест на ефективність найпоширеніших способів зробити це
EscapeNetscape

Відповіді:


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

матиме такий же ефект, використовуючи "рідний" код.

Редагувати

Так як це отримує багато думок, примітка (коментар PER @ Oriol) , що наступне більш короткий вираз ефективно еквівалентно:

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

Але зауважте в коментарі @ JussiR, що на відміну від "багатослівної" форми, вона створює порожній, невикористаний і справді непридатний екземпляр масиву в процесі. Що компілятори з цього приводу роблять поза межами програми програміста.

Редагувати

З ECMAScript 2015 (ES 6) існує також Array.from :

var arr = Array.from(htmlCollection);

Редагувати

ECMAScript 2015 також надає оператор розповсюдження , який функціонально еквівалентний Array.from(хоча зауважте, що Array.fromпідтримує функцію відображення як другий аргумент).

var arr = [...htmlCollection];

Я підтвердив, що і вище, і вище NodeList.

Порівняння продуктивності для згаданих методів: http://jsben.ch/h2IFA


7
Це не вдається в IE6.
Гірські межі

29
Ярлик [].slice.call(htmlCollection)також працює.
Оріол

1
@ChrisNielsen Так, я про це неправильно повідомив. Вибачте за розповсюдження цього навколо. Я не усвідомлював, що про це також говорив. Видалено коментар, щоб уникнути плутанини, але в контексті я десь читав (або неправильно читав), що нарізка HTMLCollection змусила його поводитись як як масив, так і колекція. Зовсім неправильно.
Ерік Реппен

3
Ярлик [] .slice не еквівалентний, оскільки він також створює невикористаний порожній екземпляр масиву. Не впевнений, чи зможуть компілятори оптимізувати його за межами.
JussiR

3
Array.fromтобто fromне підтримується IE11.
Френк Конійн

86

не впевнений, чи це найефективніше, але стислим синтаксисом ES6 може бути:

let arry = [...htmlCollection] 

Редагувати: Ще одне, з коментаря Chris_F:

let arry = Array.from(htmlCollection)

9
Крім того, ES6 додаєArray.from()
Chris_F

4
Слідкуйте за першим, є непомітна помилка при трансляції з babel, де [... htmlCollection] поверне масив із htmlCollection, оскільки це єдиний елемент.
Marcel M.

3
Оператор розповсюдження масиву не працює у htmlCollection. Це стосується лише NodeList.
Боббі

1
Array.fromтобто fromне підтримується IE11.
Френк Конійн

Benchmark Схоже, оператор розповсюдження швидше виходить з цих 2.
RedSparr0w

20

Я побачив більш стислий метод отримання Array.prototypeметодів загалом, який працює так само добре. Перетворення HTMLCollectionоб'єкта в Arrayоб'єкт показано нижче:

[] .slice.call (вашHTMLCollectionObject);

І, як згадувалося в коментарях, для старих веб-переглядачів, таких як IE7 і новіших, вам просто потрібно використовувати функцію сумісності, наприклад:

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

    return a
}

Я знаю, що це старе питання, але я вважав, що прийнята відповідь була трохи неповною; тому я подумав, що кину це FWIW.


6

Для впровадження перехресного веб-переглядача я б придумував вам переглянути функцію prototype.js $A

скопійовано з 1.6.1 :

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.sliceймовірно, тому що він доступний не в кожному браузері. Я боюсь, що продуктивність дуже погана, тому що падіння - це петля JavaScript над iterable.


2
ОП попросило іншого способу, ніж "повторення вмісту згаданої колекції та вручну переміщення кожного елемента в масив", але саме це $Aфункція виконує більшу частину часу.
Luc125

1
Я думаю, що я намагався переконатись у тому, що це не приємний спосіб зробити це, код prototype.js показує, що ви можете шукати метод 'toArray', але не в змозі ітерації найбезпечнішого маршруту
Гарет Девіс

1
Це створить нових невизначених членів у розріджених масивах. Перед призначенням повинен бути тест hasOwnProperty .
RobG

3

Це моє особисте рішення, засноване на інформації тут (цієї теми):

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

Де $ A описав Гарет Девіс у своєму дописі:

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

Якщо браузер підтримує найкращий спосіб, добре, інакше буде використовувати перехресний браузер.


Взагалі, я не очікую, що спроба "ловити" буде ефективним способом управління потоком управління. Ви можете перевірити, чи функція існує спочатку, а потім запустити одну чи іншу трохи дешевше.
Патрік

2
Як і у відповіді Гарета Девіса, це створює нових, невизначених членів у розріджених масивах, так і [,,]стає [undefined, undefined].
RobG

Такого роду проблем у мене ще не було. Він зшиває результати колекції з 3 елементів у масив із 2-х елементів. Щодо порожнього не визначеного, це обмеження JavaScript, я думаю, ви очікували нуля замість невизначеного, правда?
Густаво

3

Це працює у всіх браузерах, включаючи більш ранні версії IE.

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

Оскільки jsperf на даний момент ще не працює, ось jsfiddle, який порівнює ефективність різних методів. https://jsfiddle.net/qw9qf48j/


спробуйтеvar args = (htmlCollection.length === 1 ? [htmlCollection[0]] : Array.apply(null, htmlCollection));
Шахар Шокрані

3

Щоб ефективно перетворити масив у масив, ми можемо скористатись jQuery makeArray :

makeArray: Перетворення об’єкта, подібного до масиву, у справжній масив JavaScript.

Використання:

var domArray = jQuery.makeArray(htmlCollection);

Трохи додатково:

Якщо ви не хочете зберігати посилання на об’єкт масиву (більшість часу HTMLCollections динамічно змінюється, тому краще скопіювати їх в інший масив. Цей приклад зверніть пильну увагу на продуктивність:

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

Що таке масив?

HTMLCollection є "array-like"об'єктом, масив подібних об'єктів схожий на об'єкт масиву , але відсутній багато його визначення функціонально:

Об'єкти, схожі на масив, виглядають як масиви. Вони мають різні нумеровані елементи та властивість довжини. Але на цьому схожість зупиняється. Об'єкти, схожі на масив, не мають жодної з функцій Array, а цикли для входу навіть не працюють!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.