Каков наилучший способ перебрать набор элементов в JavaScript? - PullRequest
50 голосов
/ 01 октября 2008

В прошлом и в большинстве моих текущих проектов я обычно использовал цикл for следующим образом:

var elements = document.getElementsByTagName('div');
for (var i=0; i<elements.length; i++) {
    doSomething(elements[i]);
}

Я слышал, что использование цикла "наоборот пока" быстрее, но у меня нет реального способа подтвердить это:

var elements = document.getElementsByTagName('div'), 
    length = elements.length;

while(length--) {
    doSomething(elements[length]);
}

Что считается наилучшей практикой, когда дело доходит до зацикливания элементов в JavaScript или любого другого массива в этом отношении?

Ответы [ 14 ]

54 голосов
/ 07 января 2011

Вот хорошая форма цикла, который я часто использую. Вы создаете итеративную переменную из оператора for, и вам не нужно проверять свойство длины, которое может быть особенно дорогостоящим, особенно при итерации по NodeList. Однако, вы должны быть осторожны , вы не можете использовать его, если любое из значений в массиве может быть "ложным" . На практике я использую его только при переборе массива объектов, который не содержит нулей (например, NodeList). Но я люблю его синтаксический сахар.

var list = [{a:1,b:2}, {a:3,b:5}, {a:8,b:2}, {a:4,b:1}, {a:0,b:8}];

for (var i=0, item; item = list[i]; i++) {
  // Look no need to do list[i] in the body of the loop
  console.log("Looping: index ", i, "item" + item);
}

Обратите внимание, что это также может быть использовано для обратной петли (если ваш список не имеет свойства ['-1'])

var list = [{a:1,b:2}, {a:3,b:5}, {a:8,b:2}, {a:4,b:1}, {a:0,b:8}];

for (var i = list.length - 1, item; item = list[i]; i--) {
  console.log("Looping: index ", i, "item", item);
}

Обновление ES6

for...of дает вам имя, но не индекс, доступно с ES6

for (let item of list) {
    console.log("Looping: index ", "Sorry!!!", "item" + item);
}
26 голосов
/ 01 октября 2008

Обратите внимание, что в некоторых случаях вам нужно для зацикливания в обратном порядке (но тогда вы также можете использовать i--).

Например, кто-то хотел использовать новую функцию getElementsByClassName для зацикливания элементов данного класса и изменения этого класса. Он обнаружил, что только один из двух элементов был изменен (в FF3).
Это потому, что функция возвращает живой NodeList, который, таким образом, отражает изменения в дереве Dom. Прогулка по списку в обратном порядке позволила избежать этой проблемы.

var menus = document.getElementsByClassName("style2");
for (var i = menus.length - 1; i >= 0; i--)
{
  menus[i].className = "style1";
}

При увеличении прогрессии индекса, когда мы запрашиваем индекс 1, FF проверяет Dom и пропускает первый элемент с style2, который является 2-м из исходного Dom, таким образом, он возвращает 3-й начальный элемент!

9 голосов
/ 01 октября 2008

Мне нравится делать:


var menu = document.getElementsByTagName('div');
for (var i = 0; menu[i]; i++) {
     ...
}

Нет вызова длины массива на каждой итерации.

7 голосов
/ 01 октября 2008

На риск, что на меня кричат, я бы получил вспомогательную библиотеку javascript, такую ​​как jquery или prototype , они инкапсулируют логику в хороших методах - оба имеют метод / итератор .each чтобы сделать это - и они оба стремятся сделать это кросс-браузер совместимым

РЕДАКТИРОВАТЬ: Этот ответ был опубликован в 2008 году. Сегодня существуют гораздо лучшие конструкции. Этот конкретный случай может быть решен с помощью .forEach.

6 голосов
/ 16 марта 2016

У меня ранее была очень похожая проблема с document.getElementsByClassName (). Я не знал, что такое нодлист в то время.

var elements = document.getElementsByTagName('div');
for (var i=0; i<elements.length; i++) {
    doSomething(elements[i]);
}

Моя проблема заключалась в том, что я ожидал, что элементы будут массивом, но это не так. Список узлов Document.getElementsByTagName () возвращает итерацию, но вы не можете вызывать для него методы array.prototype.

Вы можете , однако заполните массив элементами списка узлов, как это:

var myElements = [];
for (var i=0; i<myNodeList.length; i++) {                               
    var element = myNodeList[i];
    myElements.push(element);
};

После этого вы можете свободно вызывать .innerHTML или .style или что-то еще для элементов вашего массива.

6 голосов
/ 01 октября 2008

Я думаю, что использование первой формы - это, вероятно, правильный путь, поскольку, вероятно, это самая распространенная структура цикла в известной вселенной, и, поскольку я не верю, что обратный цикл экономит ваше время в реальности увеличение / уменьшение и сравнение на каждой итерации).

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

4 голосов
/ 02 октября 2008

Также см. Мой комментарий к тесту Эндрю Хеджеса ...

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

И, увы, неудивительно, что три протестированных мною браузера показали очень разные результаты, хотя оптимизированная простая итерация была самой быстрой во всех! -)

Тест:

Массив с 500 000 элементов, созданный вне реального теста, для каждой итерации раскрывается значение определенного элемента массива.

Тестовый прогон 10 раз.

IE6:

Результаты:

Простой: 984 922 937 984 891 907 906 891 906 906

Среднее: 923,40 мс.

Оптимизировано: 766 766 844 797 750 750 750 765 766 766 766

Среднее: 773,50 мс.

Обратный ход / время: 3375,1328,1516,1344,1375,1406,1688,1344,1297,1265

Среднее: 1593,80 мс. (Обратите внимание на один особенно неловкий результат)

Опера 9,52:

Результаты:

Простой: 344 343 344 359 343 359 344 359 359 359

Среднее: 351,30 мс.

Оптимизировано: 281 297 297 297 297 281 281 297 281 281

Среднее: 289,00 мс

Обратный ход / время: 391 407 391 391 500 407 407 406 406 406

Среднее: 411,20 мс.

FireFox 3.0.1:

Результаты:

Простой: 278,251,259,245,243,242,259,246,247,256

Среднее: 252,60 мс.

Оптимизировано: 267,222,223,226,223,230,221,231,224,230

Среднее: 229,70 мс.

Обратный ход / время: 414 381 389 383 388 389 381 387 400 379

Среднее: 389,10 мс.

4 голосов
/ 01 октября 2008

Я тоже советую использовать простой способ (ПОЦЕЛУЙ! -)

- но можно найти некоторую оптимизацию, а именно не проверять длину массива более одного раза:

var elements = document.getElementsByTagName('div');
for (var i=0, im=elements.length; im>i; i++) {
    doSomething(elements[i]);
}
3 голосов
/ 04 января 2015

Форма петли предоставлена ​​ Хуаном Мендесом очень полезна и практична, Я немного его изменил, чтобы теперь он работал со строками - false, null, zero и empty.

var items = [
    true,
    false,
    null,
    0,
    ""
];

for(var i = 0, item; (item = items[i]) !== undefined; i++)
{
    console.log("Index: " + i + "; Value: " + item);
}
1 голос
/ 01 октября 2008

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

...