Для каждого над массивом в JavaScript


4685

Как я могу перебрать все записи в массиве, используя JavaScript?

Я думал, что это было что-то вроде этого:

forEach(instance in theArray)

Где theArrayмой массив, но это, кажется, неправильно.


16
Я действительно искал это, но я искал forEach и не только for. как уже говорилось, в C # это было немного по-другому, и это меня смутило :)
Dante1986

6
ECMAScript & nbsp; 6 может содержать конструкцию «for ... of». Смотрите для ... of (MDN) для получения дополнительной информации. Вы уже можете попробовать это с последними версиями Firefox.
Slaven Rezic

36
Array.ForEach примерно на 95% медленнее, чем for () для каждого массива в JavaScript. Смотрите этот тест производительности онлайн: jsperf.com/fast-array-foreach via coderwall.com/p/kvzbpa
molokoloco

77
Для многих ситуаций, что на 95% медленнее, не будет значительным blog.niftysnippets.org/2012/02/foreach-and-runtime-cost.html
Дэвид Сайкс

7
В отличие от этого , в Python является более эффективным , чтобы использовать функции , чем использование традиционных для петель. (Учтите , что i < lenи i++может быть сделано с помощью двигателя, а не переводчик.)
joeytwiddle

Ответы:


7023

TL; DR

  • Не используйте его, for-inесли не используете его с мерами предосторожности или, по крайней мере, не знаете, почему он может вас укусить.
  • Ваши лучшие ставки обычно

    • for-ofцикл (ES2015 + только),
    • Array#forEach( spec| MDN) (или его родственники someи тому подобное) (только ES5 +),
    • простая старомодная forпетля,
    • или for-inс гарантиями.

Но есть еще много интересного, читайте дальше ...


JavaScript имеет мощную семантику для циклического перемещения по массивам и объектам, похожим на массивы. Я разделил ответ на две части: параметры для подлинных массивов и параметры для объектов, похожих на массивы , таких как argumentsобъект, другие итерируемые объекты (ES2015 +), коллекции DOM и т. Д.

Я быстро отметить , что вы можете использовать ES2015 варианты в настоящее время , даже на двигателях ES5, по transpiling ES2015 в ES5. Искать "ES2015 transpiling" / "ES6 transpiling" для получения дополнительной информации ...

Хорошо, давайте посмотрим на наши варианты:

Для фактических массивов

У вас есть три варианта в ECMAScript 5 («ES5»), наиболее широко поддерживаемая на данный момент версия, и еще две, добавленные в ECMAScript 2015 («ES2015», «ES6»):

  1. Использование forEachи связанные (ES5 +)
  2. Используйте простой forцикл
  3. Используйте for-in правильно
  4. Использовать for-of(неявно использовать итератор) (ES2015 +)
  5. Используйте итератор явно (ES2015 +)

Подробности:

1. Использование forEachи связанные

В любой неопределенно современной среде (например, не в IE8), где у вас есть доступ к Arrayфункциям, добавленным ES5 (напрямую или с использованием полифиллов), вы можете использовать forEach( spec| MDN):

var a = ["a", "b", "c"];
a.forEach(function(entry) {
    console.log(entry);
});

forEachпринимает функцию обратного вызова и, необязательно, значение для использования, как thisпри вызове этого обратного вызова (не используется выше). Обратный вызов вызывается для каждой записи в массиве, чтобы пропустить несуществующие записи в разреженных массивах. Хотя выше я использовал только один аргумент, обратный вызов вызывается с тремя: значением каждой записи, индексом этой записи и ссылкой на массив, по которому вы перебираете (в случае, если ваша функция еще не имеет этого под рукой) ).

Если вы не поддерживаете устаревшие браузеры, такие как IE8 (на долю которого NetApps приходится чуть более 4% рынка на момент написания этой статьи в сентябре 2016 года), вы можете без проблем использовать forEachвеб-страницу общего назначения без прокладки. Если вам требуется поддержка устаревших браузеров, forEachлегко выполнить shimming / polyfilling (найдите « es5 shim » для нескольких вариантов).

forEach Преимущество заключается в том, что вам не нужно объявлять переменные индексации и значений в содержащей области, так как они передаются в качестве аргументов функции итерации и так хорошо подходят только для этой итерации.

Если вас беспокоит стоимость выполнения вызова функции для каждой записи массива, не беспокойтесь; подробности .

Кроме того, forEachесть функция «проходить через них все», но ES5 определила несколько других полезных функций «прорабатывать массив и делать вещи», включая:

  • every(перестает зацикливаться в первый раз, когда возвращается обратный вызов falseили что-то не так)
  • some(перестает зацикливаться при первом возврате trueили обратном вызове)
  • filter(создает новый массив, включающий элементы, в которые возвращает функция фильтра, trueи пропускает те, в которых она возвращается false)
  • map (создает новый массив из значений, возвращаемых обратным вызовом)
  • reduce (создает значение путем многократного вызова обратного вызова, передавая предыдущие значения; подробности см. в спецификации; полезно для суммирования содержимого массива и многих других)
  • reduceRight(нравится reduce, но работает в порядке убывания, а не в порядке возрастания)

2. Используйте простой forцикл

Иногда старые способы являются лучшими:

var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
    console.log(a[index]);
}

Если длина массива не будет меняться в течение цикла, и это в исполнении чувствительной коды (маловероятно), чуть более сложный вариант захвата длиной фронта может быть крошечными немного быстрее:

var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
    console.log(a[index]);
}

И / или считая в обратном направлении:

var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
    console.log(a[index]);
}

Но с современными JavaScript-движками вам редко приходится вытаскивать последний кусок сока.

В ES2015 и выше вы можете сделать свои переменные индекса и значения локальными для forцикла:

let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
    let value = a[index];
    console.log(index, value);
}
//console.log(index);   // would cause "ReferenceError: index is not defined"
//console.log(value);   // would cause "ReferenceError: value is not defined"

И когда вы делаете это, не только, valueно и indexвоссоздается для каждой итерации цикла, то есть замыкания, созданные в теле цикла, сохраняют ссылку на indexvalue), созданный для этой конкретной итерации:

let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
    divs[index].addEventListener('click', e => {
        console.log("Index is: " + index);
    });
}

Если бы у вас было пять делений, вы получили бы «Индекс: 0», если вы щелкнули по первому, и «Индекс: 4», если вы щелкнули по последнему. Это не работает, если вы используете varвместо let.

3. Используйте for-in правильно

Вы получите людей, говорящих вам, чтобы использовать for-in, но это не то for-in, для чего . for-inциклически перебирает перечисляемые свойства объекта , а не индексы массива. Заказ не гарантирован , даже в ES2015 (ES6). ES2015 + действительно определяет порядок свойств объекта (через [[OwnPropertyKeys]], [[Enumerate]]и вещи, которые используют их как Object.getOwnPropertyKeys), но он не определил, что for-inбудет следовать этому порядку; ES2020 сделал, хотя. (Подробности в этом другом ответе .)

Единственными реальными вариантами использования для for-inмассива являются:

  • Это редкие массивы с большими пробелами в них, или
  • Вы используете неэлементные свойства и хотите включить их в цикл

Рассмотрим только первый пример: вы можете использовать for-inэти редкие элементы массива, если используете соответствующие меры безопасности:

// `a` is a sparse array
var key;
var a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (key in a) {
    if (a.hasOwnProperty(key)  &&        // These checks are
        /^0$|^[1-9]\d*$/.test(key) &&    // explained
        key <= 4294967294                // below
        ) {
        console.log(a[key]);
    }
}

Обратите внимание на три проверки:

  1. Что у объекта есть собственное свойство с таким именем (не то, которое он наследует от своего прототипа), и

  2. Что ключом являются все десятичные цифры (например, обычная строковая форма, а не научная запись), и

  3. Что значение ключа при наведении на число составляет <= 2 ^ 32 - 2 (что составляет 4 294 967 294). Откуда этот номер? Это часть определения индекса массива в спецификации . Другие числа (нецелые числа, отрицательные числа, числа больше 2 ^ 32 - 2) не являются индексами массива. Причина , это 2 ^ 32 - 2 такова, что делает наибольшее значение индекса один ниже , чем 2 ^ 32 - 1 , которое является максимальным значением массива - х lengthможет иметь. (Например, длина массива соответствует 32-разрядному целому числу без знака.) (Пропишет в RobG, указав в комментарии к моему сообщению в блоге, что мой предыдущий тест был не совсем правильным.)

Конечно, вы бы не сделали этого в встроенном коде. Вы бы написали служебную функцию. Может быть:

4. Используйте for-of(неявно используйте итератор) (ES2015 +)

ES2015 добавил итераторы в JavaScript. Самый простой способ использовать итераторы - это новый for-ofоператор. Это выглядит так:

const a = ["a", "b", "c"];
for (const val of a) {
    console.log(val);
}

Под прикрытием он получает итератор из массива и проходит по нему, получая значения из него. Это не имеет проблемы, которую for-inимеет использование с использованием , потому что он использует итератор, определенный объектом (массивом), и массивы определяют, что их итераторы выполняют итерацию через свои записи (не их свойства). В отличие от for-inES5, порядок посещения записей - это порядковый номер их индексов.

5. Используйте итератор явно (ES2015 +)

Иногда вам может понадобиться явно использовать итератор . Вы тоже можете это сделать, хотя это намного более неприятно, чем . Это выглядит так:for-of

const a = ["a", "b", "c"];
const it = a.values();
let entry;
while (!(entry = it.next()).done) {
    console.log(entry.value);
}

Итератор - это объект, соответствующий определению Итератора в спецификации. Его nextметод возвращает новый объект результата каждый раз, когда вы вызываете его. У объекта результата есть свойство, doneсообщающее нам, сделано ли это, и свойство valueсо значением для этой итерации. ( doneнеобязательно, если будет false, valueнеобязательно, если будет undefined.)

Значение valueварьируется в зависимости от итератора; Массивы поддерживают (как минимум) три функции, которые возвращают итераторы:

  • values()Это тот, который я использовал выше. Она возвращает итератор , где каждый valueявляется элементом массива для этой итерации ( "a", "b"и "c"в примере выше).
  • keys()Возвращает итератор, где каждый valueявляется ключом для этой итерации (так для нашего aвыше, это было бы "0"тогда "1", потом "2").
  • entries()Возвращает итератор, где каждый valueявляется массивом в форме [key, value]для этой итерации.

Для массивоподобных объектов

Помимо истинных массивов, существуют также массоподобные объекты, у которых есть lengthсвойство и свойства с числовыми именами: NodeListэкземпляры, argumentsобъект и т. Д. Как мы перебираем их содержимое?

Используйте любой из параметров выше для массивов

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

  1. Использование forEachи связанные (ES5 +)

    Различные функции Array.prototypeявляются «преднамеренно общими» и обычно могут использоваться в объектах, подобных массивам, через Function#callили Function#apply. (См. Caveat для предоставленных хостом объектов в конце этого ответа, но это редкая проблема.)

    Предположим , вы хотите использовать forEachна множестве А Node«S childNodesсобственности. Вы бы сделали это:

    Array.prototype.forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });
    

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

    // (This is all presumably in some scoping function)
    var forEach = Array.prototype.forEach;
    
    // Then later...
    forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });
    
  2. Используйте простой forцикл

    Очевидно, что простой forцикл применяется к объектам, подобным массиву.

  3. Используйте for-in правильно

    for-inс теми же средствами защиты, что и с массивом, должны работать и с объектами, подобными массиву; может применяться предостережение для предоставленных хостом объектов на # 1 выше.

  4. Использовать for-of(неявно использовать итератор) (ES2015 +)

    for-ofиспользует итератор, предоставленный объектом (если есть). Это включает предоставленные хостом объекты. Например, спецификация для NodeListfrom querySelectorAllбыла обновлена ​​для поддержки итерации. Спец для HTMLCollectionот getElementsByTagNameне было.

  5. Используйте итератор явно (ES2015 +)

    Смотрите № 4.

Создать истинный массив

В других случаях вы можете захотеть преобразовать подобный массиву объект в истинный массив. Делать это на удивление легко:

  1. Используйте sliceметод массивов

    Мы можем использовать sliceметод массивов, который, как и другие методы, упомянутые выше, является «преднамеренно родовым» и поэтому может использоваться с объектами, похожими на массивы, например так:

    var trueArray = Array.prototype.slice.call(arrayLikeObject);

    Так, например, если мы хотим преобразовать a NodeListв истинный массив, мы могли бы сделать это:

    var divs = Array.prototype.slice.call(document.querySelectorAll("div"));

    Смотрите Caveat для предоставленных хостом объектов ниже. В частности, обратите внимание, что это не удастся в IE8 и более ранних версиях, которые не позволяют вам использовать предоставленные хостом объекты, как thisэто.

  2. Использовать распространенный синтаксис ( ...)

    Также возможно использовать синтаксис распространения ES2015 с механизмами JavaScript, которые поддерживают эту функцию. Например for-of, здесь используется итератор, предоставленный объектом (см. # 4 в предыдущем разделе):

    var trueArray = [...iterableObject];

    Так, например, если мы хотим преобразовать a NodeListв истинный массив, с распространенным синтаксисом это становится довольно кратким:

    var divs = [...document.querySelectorAll("div")];
  3. использование Array.from

    Array.from (спец) | (MDN) (ES2015 +, но легко заполняемый) создает массив из объекта, подобного массиву, при необходимости сначала пропуская записи через функцию отображения. Так:

    var divs = Array.from(document.querySelectorAll("div"));

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

    // Arrow function (ES2015):
    var divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
    
    // Standard function (since `Array.from` can be shimmed):
    var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {
        return element.tagName;
    });
    

Предостережение для предоставленных хостом объектов

Если вы используете Array.prototypeфункции с предоставленными хостом массивоподобными объектами (списки DOM и другие вещи, предоставляемые браузером, а не механизмом JavaScript), вам нужно обязательно протестировать в своих целевых средах, чтобы убедиться, что предоставленный хостом объект ведет себя правильно , Большинство ведут себя правильно (сейчас), но важно проверить. Причина в том, что большинство Array.prototypeметодов, которые вы, вероятно, захотите использовать, полагаются на предоставленный хостом объект, дающий честный ответ на абстрактную [[HasProperty]]операцию. На момент написания статьи браузеры отлично справлялись с этой задачей, но спецификация 5.1 действительно допускала вероятность того, что предоставленный хостом объект может быть не честным. Это в §8.6.2 , несколько параграфов ниже большой таблицы в начале этого раздела), где написано:

Хост-объекты могут реализовывать эти внутренние методы любым способом, если не указано иное; например, одна возможность состоит в том, что [[Get]]и [[Put]]для конкретного хост-объекта действительно извлекаются и сохраняются значения свойств, но [[HasProperty]]всегда генерируется значение false .

(Я не мог найти эквивалентную формулировку в ES2015 спецификации, но это связано , по - прежнему имеет место.) Опять же , как и на момент написания этого общего хоста предоставляется массив типа объектов в современных браузерах [ NodeListслучаях, например] сделать ручку [[HasProperty]]правильно, но важно проверить.)


44
Я также хотел бы добавить, что .forEachэто не может быть эффективно сломано. Вы должны бросить исключение, чтобы выполнить перерыв.
Пиюсн

82
@Pius: Если вы хотите разорвать цикл, вы можете использовать some. (Я бы предпочел также разрешить взлом forEach, но они меня не спрашивали. ;-))
TJ Crowder

6
@TJCrowder Правда, хотя это выглядит скорее как обходной путь, поскольку это не его основная цель.
Пиюсн

8
@ user889030: Вам нужно ,после k=0, а не ;. Помните, программирование - это много вещей, одним из которых является пристальное внимание к деталям ... :-)
TJ Crowder

5
@JimB: это описано выше (и lengthэто не метод). :-)
TJ Crowder

513

Примечание . Этот ответ безнадежно устарел. Для более современного подхода посмотрите на методы, доступные в массиве . Интересующие методы могут быть:

  • для каждого
  • карта
  • фильтр
  • застежка-молния
  • уменьшить
  • каждый
  • несколько

Стандартный способ итерации массива в JavaScript - это vanilla for-loop:

var length = arr.length,
    element = null;
for (var i = 0; i < length; i++) {
  element = arr[i];
  // Do something with element
}

Обратите внимание, однако, что этот подход хорош, только если у вас есть плотный массив, и каждый индекс занят элементом. Если массив разреженный, то при таком подходе вы можете столкнуться с проблемами производительности, так как вы будете перебирать множество индексов, которых на самом деле нет в массиве. В этом случае for .. inлучше использовать -loop. Однако вы должны использовать соответствующие меры предосторожности, чтобы гарантировать, что только требуемые свойства массива (то есть элементы массива) будут применены, так как for..in-loop будет также перечисляться в устаревших браузерах, или если дополнительные свойства определены как enumerable,

В ECMAScript 5 для прототипа массива будет использоваться метод forEach, но он не поддерживается в устаревших браузерах. Поэтому, чтобы иметь возможность использовать его последовательно, вы должны либо иметь среду, которая его поддерживает (например, Node.js для серверного JavaScript), либо использовать «Polyfill». Polyfill для этой функциональности, однако, тривиален, и, поскольку он делает код более легким для чтения, его стоит включить в Polyfill.


2
почему for(instance in objArray) не правильное использование? это выглядит более простым для меня, но я слышал, вы говорите об этом как о неправильном способе использования?
Dante1986

21
Вы можете использовать встроенное кэширование длины: for (var i = 0, l = arr.length; i <l; i ++)
Robert Hoffmann

3
Запятая в конце первой строки является преднамеренной, или это опечатка (может быть точка с запятой)?
Мохд Абдул Муджиб

6
@ wardha-Web Это намеренно. Это позволяет нам объявлять несколько переменных одним varключевым словом. Если бы мы использовали точку с запятой, то elementбыли бы объявлены в глобальной области (или, скорее, JSHint кричал бы на нас, прежде чем он достигнет производства).
PatrikAkerstrand

239

Если вы используете библиотеку jQuery , вы можете использовать jQuery.each :

$.each(yourArray, function(index, value) {
  // do your stuff here
});

РЕДАКТИРОВАТЬ :

Что касается вопроса, пользователь хочет код в javascript вместо jquery, поэтому редактирование

var length = yourArray.length;   
for (var i = 0; i < length; i++) {
  // Do something with yourArray[i].
}

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

47
Просто ради этого: каждый jQuery намного медленнее, чем нативные решения. JQuery рекомендует использовать нативный JavaScript вместо jQuery, когда это возможно. jsperf.com/browser-diet-jquery-each-vs-for-loop
Кевин Босс,

8
Воздержитесь от использования jQuery, когда вы можете использовать vanilla js
Noe

2
Придерживайтесь стандартного JS, держите сторонние библиотеки вне ответа, если нет решения на родном языке
scubasteve

116

Петля назад

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

for (var i = array.length; i--; ) {
     // process array[i]
}

Преимущества:

  • Вам не нужно объявлять временную lenпеременную или сравнивать array.lengthна каждой итерации, что может быть минутной оптимизацией.
  • Удаление братьев и сестер из DOM в обратном порядке обычно более эффективно . (Браузер должен меньше перемещать элементы в своих внутренних массивах.)
  • Если вы изменяете массив во время цикла, по индексу i или после него (например, удаляете или вставляете элемент в array[i]), тогда цикл в прямом направлении пропускает элемент, сдвинутый влево в положение i , или повторно обрабатывает i- й элемент, который был смещено вправо. В традиционном цикле for вы можете обновить i, чтобы он указывал на следующий элемент, который нуждается в обработке - 1, но простое изменение направления итерации часто является более простым и элегантным решением .
  • Аналогичным образом, при изменении или удалении вложенных элементов DOM обратная обработка может обойти ошибки . Например, рассмотрите возможность изменения innerHTML родительского узла перед обработкой его потомков. К тому времени, когда будет достигнут дочерний узел, он будет отсоединен от DOM, будучи заменен вновь созданным дочерним, когда был написан innerHTML родителя.
  • Он короче печатать и читать , чем некоторые другие доступные опции. Хотя и проигрывает forEach()ES6 for ... of.

Недостатки:

  • Он обрабатывает элементы в обратном порядке. Если вы строили новый массив из результатов или печатали объекты на экране, естественно, результат будет обратным относительно исходного порядка.
  • Повторная вставка братьев и сестер в DOM в качестве первого ребенка для сохранения их порядка менее эффективна . (Браузер будет вынужден постоянно сдвигать вещи.) Чтобы эффективно и упорядоченно создавать узлы DOM, просто выполните цикл вперед и добавьте как обычно (а также используйте «фрагмент документа»).
  • Обратный цикл сбивает с толку молодых разработчиков. (Вы можете считать это преимуществом, в зависимости от вашего мировоззрения.)

Я должен всегда использовать это?

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

Хотя прирост производительности обычно незначителен, это своего рода крики:

«Просто сделайте это с каждым элементом в списке, мне нет дела до заказа!»

Однако на практике , что это не на самом деле является надежным показателем намерения, поскольку она ничем не отличается от тех случаев , когда вы делаете заботиться о порядке, и действительно необходимо , чтобы петли в обратном. Так что на самом деле для точного выражения намерения «все равно» потребуется другая конструкция, которая в настоящее время недоступна в большинстве языков, включая ECMAScript, но которую можно вызвать, например forEachUnordered(),.

Если порядок не имеет значения, и эффективность является проблемой (в самом внутреннем цикле игрового или анимационного движка), тогда может быть приемлемым использовать цикл for в качестве обратного шаблона. Просто помните, что просмотр обратного цикла for в существующем коде не обязательно означает, что порядок не имеет значения!

Лучше было использовать forEach ()

В целом для кода более высокого уровня, где ясность и безопасность важнее, я ранее рекомендовал использовать в Array::forEachкачестве шаблона по умолчанию для циклов (хотя в эти дни я предпочитаю использовать for..of). Причины, чтобы отдать предпочтение forEachперед обратным циклом:

  • Это яснее читать.
  • Это указывает на то, что я не собираюсь сдвигаться в блоке (что всегда является возможным сюрпризом, скрывающимся в длинных forи whileпетлях).
  • Это дает вам свободную возможность для закрытия.
  • Это уменьшает утечку локальных переменных и случайное столкновение с (и мутацией) внешних переменных.

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

(Если обсуждение намерений не имеет смысла для вас, тогда вы и ваш код можете получить пользу от просмотра лекции Крокфорда « Стиль программирования и ваш мозг» .)

Теперь еще лучше использовать для ... из!

Существует дискуссия о том , for..ofили forEach()предпочтительнее:

Лично я склонен использовать все, что выглядит проще для чтения, если только производительность или минимизация не стали серьезной проблемой. Поэтому в эти дни я предпочитаю использовать for..ofвместо forEach(), но я всегда буду использовать mapили filterили findили someкогда это применимо. (Ради своих коллег я редко пользуюсь reduce.)


Как это работает?

for (var i = 0; i < array.length; i++) { ... }   // Forwards

for (var i = array.length; i--; )    { ... }   // Reverse

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

  • Как это может начаться array.lengthбез взрыва?

    Поскольку i--выполняется перед каждой итерацией, на первой итерации мы фактически получим доступ к элементу, array.length - 1что позволяет избежать любых проблем с элементами Array-out-of-bounds undefined .

  • Почему он не прекращает итерации до индекса 0?

    Цикл прекратит итерацию, когда условие i--оценивается как ложное значение (когда оно возвращает 0).

    Хитрость в том, что в отличие --iот конечного i--оператора декремент уменьшается, iно выдает значение перед декрементом. Ваша консоль может продемонстрировать это:

    > var i = 5; [i, i--, i];

    [5, 5, 4]

    Таким образом, на последней итерации я был ранее равен 1, и i--выражение меняет его на 0, но на самом деле возвращает 1 (правда), и поэтому условие выполняется. На следующей итерации значение ii-- изменяется на -1, но возвращается 0 (фальси), в результате чего выполнение немедленно выпадает из нижней части цикла.

    В традиционных вперед для цикла, i++и ++iявляются взаимозаменяемыми (как Дуглас Крокфорд указывает). Однако в обратном цикле for, поскольку наш декремент также является условным выражением, мы должны придерживаться его, i--если хотим обработать элемент с индексом 0.


пустяки

Некоторые люди любят рисовать маленькую стрелку в обратном forцикле и заканчивают миганием:

for (var i = array.length; i --> 0 ;) {

Кредиты идут в WYL для того, чтобы показать мне преимущества и ужасы обратного цикла.


3
Я забыл добавить тесты . Я также забыл упомянуть, что обратная петля является значительной оптимизацией для 8-разрядных процессоров, таких как 6502, где вы действительно получаете сравнение бесплатно!
Joeytwiddle

Тот же ответ дается гораздо более кратко здесь (на другой вопрос).
Joeytwiddle

84

Некоторые языки C- стиля используют foreachдля циклического перебора перечислений. В JavaScript это делается с помощью for..inструктуры цикла :

var index,
    value;
for (index in obj) {
    value = obj[index];
}

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

for (i in obj) {
    if (obj.hasOwnProperty(i)) {
        //do stuff
    }
}

Кроме того, в ECMAScript 5 добавлен forEachметод, Array.prototypeкоторый можно использовать для перечисления по массиву с помощью функции обратного вызова (polyfill находится в документации, поэтому вы все равно можете использовать его для старых браузеров):

arr.forEach(function (val, index, theArray) {
    //do stuff
});

Важно отметить, что Array.prototype.forEachон не прерывается, когда возвращается обратный вызов false. jQuery и Underscore.js предоставляют свои собственные варианты eachдля обеспечения циклов, которые могут быть закорочены.


3
Итак, как же вырваться из цикла foreach ECMAScript5, как мы это сделали бы для цикла for for или цикла foreach, подобного тому, который есть в языках стиля C?
Кьяран Галлахер

5
@CiaranG, в JavaScript часто встречаются eachметоды, позволяющие return falseиспользовать их для выхода из цикла, но forEachэто не вариант. Можно использовать внешний флаг (т . if (flag) return;forEach
Е.

43

Если вы хотите зациклить массив, используйте стандартный forцикл из трех частей .

for (var i = 0; i < myArray.length; i++) {
    var arrayItem = myArray[i];
}

Вы можете добиться некоторой оптимизации производительности, кэшируя myArray.lengthили перебирая ее в обратном порядке.


6
потому что (var i = 0, length = myArray.length; i <length; i ++) должны это сделать
Эдсон Медина,

5
@EdsonMedina Это также создаст новую глобальную переменную с именем length. ;)
joeytwiddle

4
@joeytwiddle Да, но это выходит за рамки этого поста. Вы все равно создадите глобальную переменную.
Эдсон Медина

6
@EdsonMedina Мои извинения, я ошибся. Использование ,после назначения не вводит новый глобальный, так что ваше предложение просто отлично ! Я путал это с другой проблемой: использование =после присваивания создает новый глобал.
Joeytwiddle

Остерегайтесь, переменная i не является локальной для цикла. У JavaScript нет блочной области видимости. Вероятно, лучше объявить var i, length, arrayItem;перед циклом, чтобы избежать этого недоразумения.
Джеймс

35

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

angular.forEachпринимает 2 аргумента и необязательный третий аргумент. Первый аргумент - это объект (массив) для итерации, второй аргумент - это функция итератора, а необязательный третий аргумент - это контекст объекта (в основном называемый внутри цикла как «this»).

Существуют разные способы использования цикла forEach angular. Самый простой и, вероятно, самый используемый

var temp = [1, 2, 3];
angular.forEach(temp, function(item) {
    //item will be each element in the array
    //do something
});

Еще один способ, который полезен для копирования элементов из одного массива в другой,

var temp = [1, 2, 3];
var temp2 = [];
angular.forEach(temp, function(item) {
    this.push(item); //"this" refers to the array passed into the optional third parameter so, in this case, temp2.
}, temp2);

Хотя вам не нужно этого делать, вы можете просто сделать следующее, и это эквивалентно предыдущему примеру:

angular.forEach(temp, function(item) {
    temp2.push(item);
});

Теперь есть плюсы и минусы использования этой angular.forEachфункции в отличие от встроенного forцикла со вкусом ванили .

Pros

  • Легко читаемость
  • Простота записи
  • Если доступно, angular.forEachбудет использовать цикл ES5 forEach. Теперь я доберусь до эффективности в разделе «против», поскольку циклы forEach намного медленнее, чем циклы for. Я упоминаю это как профессионал, потому что приятно быть последовательным и стандартизированным.

Рассмотрим следующие 2 вложенных цикла, которые делают одно и то же. Допустим, у нас есть 2 массива объектов, и каждый объект содержит массив результатов, каждый из которых имеет свойство Value, являющееся строкой (или чем-то еще). И скажем, нам нужно перебрать каждый из результатов и, если они равны, выполнить некоторое действие:

angular.forEach(obj1.results, function(result1) {
    angular.forEach(obj2.results, function(result2) {
        if (result1.Value === result2.Value) {
            //do something
        }
    });
});

//exact same with a for loop
for (var i = 0; i < obj1.results.length; i++) {
    for (var j = 0; j < obj2.results.length; j++) {
        if (obj1.results[i].Value === obj2.results[j].Value) {
            //do something
        }
    }
}

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

Cons

  • Эффективность. angular.forEachи нативный forEach, в этом отношении, оба намного медленнее, чем нормальный forцикл ... примерно на 90% медленнее . Поэтому для больших наборов данных лучше придерживаться собственного forцикла.
  • Без перерыва, продолжения или возврата поддержки. continueна самом деле поддерживается « случайностью », для продолжения angular.forEachвы просто помещаете return;в функцию оператор типа, angular.forEach(array, function(item) { if (someConditionIsTrue) return; });который заставит его выйти из функции для этой итерации. Это также связано с тем, что натив forEachне поддерживает перерыв или продолжить.

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


34

Если вы не против очистки массива:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

xбудет содержать последнее значение yи будет удалено из массива. Вы также можете использовать shift()который даст и удалит первый элемент из y.


4
Это не сработает, если у вас получится разреженный массив, например [1, 2, undefined, 3].
М. Grzywaczewski

2
... или действительно что-нибудь фальши: [1, 2, 0, 3]или[true, true, false, true]
joeytwiddle

31

Foreach реализации ( см в jsFiddle ):

function forEach(list,callback) {
  var length = list.length;
  for (var n = 0; n < length; n++) {
    callback.call(list[n]);
  }
}

var myArray = ['hello','world'];

forEach(
  myArray,
  function(){
    alert(this); // do something
  }
);

2
Итератор в этом, делает ненужный расчет длины. В идеальном случае длина списка должна рассчитываться только один раз.
Мидхун Кришна

2
@MidhunKrishna Я обновил свой ответ и jsFiddle, но помните, что это не так просто, как вы думаете. Проверьте этот вопрос
nmoliveira

2
Полную и правильную реализацию можно найти здесь: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
marciowb

29

Вероятно, for(i = 0; i < array.length; i++)петля не лучший выбор. Почему? Если у вас есть это:

var array = new Array();
array[1] = "Hello";
array[7] = "World";
array[11] = "!";

Метод будет вызываться из array[0]в array[2]. Во-первых, это будет сначала ссылаться на переменные, которых у вас даже нет, во-вторых, у вас не будет переменных в массиве, и в-третьих, это сделает код более смелым. Посмотрите здесь, это то, что я использую:

for(var i in array){
    var el = array[i];
    //If you want 'i' to be INT just put parseInt(i)
    //Do something with el
}

И если вы хотите, чтобы это была функция, вы можете сделать это:

function foreach(array, call){
    for(var i in array){
        call(array[i]);
    }
}

Если вы хотите сломаться, немного больше логики:

function foreach(array, call){
    for(var i in array){
        if(call(array[i]) == false){
            break;
        }
    }
}

Пример:

foreach(array, function(el){
    if(el != "!"){
        console.log(el);
    } else {
        console.log(el+"!!");
    }
});

Возвращает:

//Hello
//World
//!!!

29

Есть три реализации foreachв JQuery следующим образом .

var a = [3,2];

$(a).each(function(){console.log(this.valueOf())}); //Method 1
$.each(a, function(){console.log(this.valueOf())}); //Method 2
$.each($(a), function(){console.log(this.valueOf())}); //Method 3

2
Консольный журнал предназначен только для демонстрации. Это сделано для того, чтобы он стал примером для бега.
Раджеш Пол

29

Начиная с ECMAScript 6:

list = [0, 1, 2, 3]
for (let obj of list) {
    console.log(obj)
}

Где ofизбегает странностей, связанных с inи заставляет его работать как forцикл любого другого языка, и letсвязывается iвнутри цикла, а не внутри функции.

Скобки ( {}) могут быть опущены, когда есть только одна команда (например, в примере выше).


28

Простым решением сейчас будет использование библиотеки underscore.js . Он предоставляет много полезных инструментов, таких как eachи автоматически делегирует работу нативной, forEachесли она доступна.

Пример CodePen о том, как это работает:

var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});

Смотрите также


23

Там нет никакого for eachцикла в нативном JavaScript . Вы можете использовать библиотеки, чтобы получить эту функциональность (я рекомендую Underscore.js ), используйте простой forцикл.

for (var instance in objects) {
   ...
}

Однако обратите внимание, что могут быть причины использовать еще более простой forцикл (см. Вопрос переполнения стека. Почему использование «for… in» с итерацией массива является плохой идеей? )

var instance;
for (var i=0; i < objects.length; i++) {
    var instance = objects[i];
    ...
}

22

Это итератор для не разреженного списка, где индекс начинается с 0, что является типичным сценарием при работе с document.getElementsByTagName или document.querySelectorAll)

function each( fn, data ) {

    if(typeof fn == 'string')
        eval('fn = function(data, i){' + fn + '}');

    for(var i=0, L=this.length; i < L; i++) 
        fn.call( this[i], data, i );   

    return this;
}

Array.prototype.each = each;  

Примеры использования:

Пример № 1

var arr = [];
[1, 2, 3].each( function(a){ a.push( this * this}, arr);
arr = [1, 4, 9]

Пример № 2

each.call(document.getElementsByTagName('p'), "this.className = data;",'blue');

Каждый р-тег получает class="blue"

Пример № 3

each.call(document.getElementsByTagName('p'), 
    "if( i % 2 == 0) this.className = data;",
    'red'
);

Каждый другой тег p получает class="red">

Пример № 4

each.call(document.querySelectorAll('p.blue'), 
    function(newClass, i) {
        if( i < 20 )
            this.className = newClass;
    }, 'green'
);

И, наконец, первые 20 синих p-тегов заменены на зеленые.

Предостережение при использовании строки в качестве функции: функция создается вне контекста и должна использоваться только в том случае, если вы уверены в области видимости переменной. В противном случае лучше передавать функции там, где обзор более интуитивно понятен.


21

Есть несколько способов перебрать массив в JavaScript, как показано ниже:

ибо - это самый распространенный . Полный блок кода для зацикливания

var languages = ["Java", "JavaScript", "C#", "Python"];
var i, len, text;
for (i = 0, len = languages.length, text = ""; i < len; i++) {
    text += languages[i] + "<br>";
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

while - цикл пока условие выполнено. Вроде бы самая быстрая петля

var text = "";
var i = 0;
while (i < 10) {
    text +=  i + ") something<br>";
    i++;
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

do / while - также цикл по блоку кода, пока условие выполнено, будет выполняться как минимум один раз

var text = ""
var i = 0;

do {
    text += i + ") something <br>";
    i++;
}
while (i < 10);

document.getElementById("example").innerHTML = text;
<p id="example"></p>

Функциональные циклы - forEach, map, filter, и reduce(они перебрать функции, но они используются , если вам нужно что - то делать с вашим массивом и т.д.

// For example, in this case we loop through the number and double them up using the map function
var numbers = [65, 44, 12, 4];
document.getElementById("example").innerHTML = numbers.map(function(num){return num * 2});
<p id="example"></p>

Для получения дополнительной информации и примеров о функциональном программировании на массивах, смотрите пост в блоге. Функциональное программирование на JavaScript: отображение, фильтрация и уменьшение .


Небольшое исправление: forEachэто не «функциональный» цикл, поскольку он не возвращает новый Array(на самом деле он ничего не возвращает), он просто повторяется.
nicholaswmin

19

ECMAScript 5 (версия на JavaScript) для работы с массивами:

forEach - выполняет итерацию по каждому элементу в массиве и делает с каждым элементом все, что вам нужно.

['C', 'D', 'E'].forEach(function(element, index) {
  console.log(element + " is #" + (index+1) + " in the musical scale");
});

// Output
// C is the #1 in musical scale
// D is the #2 in musical scale
// E is the #3 in musical scale

На всякий случай больше интересует работа над массивом с использованием некоторой встроенной функции.

map - создает новый массив с результатом функции обратного вызова. Этот метод хорошо использовать, когда вам нужно отформатировать элементы вашего массива.

// Let's upper case the items in the array
['bob', 'joe', 'jen'].map(function(elem) {
  return elem.toUpperCase();
});

// Output: ['BOB', 'JOE', 'JEN']

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

[1,2,3,4].reduce(function(previous, current) {
  return previous + current;
});
// Output: 10
// 1st iteration: previous=1, current=2 => result=3
// 2nd iteration: previous=3, current=3 => result=6
// 3rd iteration: previous=6, current=4 => result=10

Every - Возвращает true или false, если все элементы в массиве проходят тест в функции обратного вызова.

// Check if everybody has 18 years old of more.
var ages = [30, 43, 18, 5];
ages.every(function(elem) {
  return elem >= 18;
});

// Output: false

filter - Очень похоже на все, за исключением того, что filter возвращает массив с элементами, которые возвращают true данной функции.

// Finding the even numbers
[1,2,3,4,5,6].filter(function(elem){
  return (elem % 2 == 0)
});

// Output: [2,4,6]

16

Там нет встроенной способности взломать forEach. Чтобы прервать выполнение, используйте Array#someкак ниже:

[1,2,3].some(function(number) {
    return number === 1;
});

Это работает, потому что someвозвращает true, как только любой из обратных вызовов, выполненных в порядке массива, возвращает true, замыкая выполнение остальных. Оригинальный ответ см. Прототип Array для некоторых


13

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

var foo = [object,object,object];
for (var i = foo.length, item; item = foo[--i];) {
    console.log(item);
}

Плюсы:

Преимущество для этого: у вас есть ссылка уже в первом, как, что не нужно будет объявлять позже с другой строкой. Это удобно при зацикливании через массив объектов.

Минусы:

Это будет прерываться всякий раз, когда ссылка неверна - Falsey (неопределенный и т. Д.). Это может быть использовано как преимущество, хотя. Тем не менее, это будет немного сложнее для чтения. А также, в зависимости от браузера, его можно «не» оптимизировать, чтобы он работал быстрее оригинального.


12

JQuery способ использования $.map:

var data = [1, 2, 3, 4, 5, 6, 7];

var newData = $.map(data, function(element) {
    if (element % 2 == 0) {
        return element;
    }
});

// newData = [2, 4, 6];

1
Я думаю, что выход будет более вероятным [undefined, 2, undefined, 4, undefined, 6, undefined].

10

Использование циклов с деструктуризацией ECMAScript 6 и оператором распространения

Разрушение и использование оператора распространения оказалось весьма полезным для новичков в ECMAScript 6 как более удобочитаемый / эстетичный, хотя некоторые ветераны JavaScript могут посчитать это грязным. Юниоры или другие люди могут найти это полезным.

В следующих примерах будут использоваться for...ofоператор и .forEachметод.

Примеры 6, 7 и 8 могут быть использованы с какими - либо функциональными петлями , такими как .map, .filter, .reduce, .sort, .every, .some. Для получения дополнительной информации об этих методах, проверьте Array Object .

Пример 1: Обычный for...ofцикл - здесь никаких хитростей.

let arrSimple = ['a', 'b', 'c'];

for (let letter of arrSimple) {
  console.log(letter);
}

Пример 2: разделение слов на символы

let arrFruits = ['apple', 'orange', 'banana'];

for (let [firstLetter, ...restOfTheWord] of arrFruits) {
  // Create a shallow copy using the spread operator
  let [lastLetter] = [...restOfTheWord].reverse();
  console.log(firstLetter, lastLetter, restOfTheWord);
}

Пример 3: цикл с keyиvalue

// let arrSimple = ['a', 'b', 'c'];

// Instead of keeping an index in `i` as per example `for(let i = 0 ; i<arrSimple.length;i++)`
// this example will use a multi-dimensional array of the following format type:
// `arrWithIndex: [number, string][]`

let arrWithIndex = [
  [0, 'a'],
  [1, 'b'],
  [2, 'c'],
];

// Same thing can be achieved using `.map` method
// let arrWithIndex = arrSimple.map((i, idx) => [idx, i]);

// Same thing can be achieved using `Object.entries`
// NOTE: `Object.entries` method doesn't work on Internet Explorer  unless it's polyfilled
// let arrWithIndex = Object.entries(arrSimple);

for (let [key, value] of arrWithIndex) {
  console.log(key, value);
}

Пример 4: Получить свойства объекта встроенными

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];

for (let { name, age: aliasForAge } of arrWithObjects) {
  console.log(name, aliasForAge);
}

Пример 5: Получить глубокие свойства объекта того, что вам нужно

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

for (let { name, tags: [firstItemFromTags, ...restOfTags] } of arrWithObjectsWithArr) {
  console.log(name, firstItemFromTags, restOfTags);
}

Пример 6: Используется ли Пример 3 с.forEach

let arrWithIndex = [
  [0, 'a'],
  [1, 'b'],
  [2, 'c'],
];

// Not to be confused here, `forEachIndex` is the real index
// `mappedIndex` was created by "another user", so you can't really trust it

arrWithIndex.forEach(([mappedIndex, item], forEachIndex) => {
  console.log(forEachIndex, mappedIndex, item);
});

Пример 7: Используется ли Пример 4 с.forEach

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];
// NOTE: Destructuring objects while using shorthand functions
// are required to be surrounded by parentheses
arrWithObjects.forEach( ({ name, age: aliasForAge }) => {
  console.log(name, aliasForAge)
});

Пример 8: Является ли пример 5 используется с.forEach

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

arrWithObjectsWithArr.forEach(({
  name,
  tags: [firstItemFromTags, ...restOfTags]
}) => {
  console.log(name, firstItemFromTags, restOfTags);
});


7

Самый близкий к вашей идее способ - использовать Array.forEach()функцию закрытия, которая будет выполняться для каждого элемента массива.

myArray.forEach(
  (item) => {
    // Do something
    console.log(item);
  }
);

Другим жизнеспособным способом было бы использовать Array.map()метод, который работает таким же образом, но он также принимает все возвращаемые значения и возвращает их в новом массиве (по существу, отображая каждый элемент в новый), например так:

var myArray = [1, 2, 3];
myArray = myArray.map(
  (item) => {
    return item + 1;
  }
);

console.log(myArray); // [2, 3, 4]

1
Это неправильно, mapне изменяет элементы массива, поскольку возвращает новый массив, а элементы этого нового являются результатом применения функции к элементам старого массива.
Хесус Франко

Я никогда не говорил, что он не возвращает новый массив, я имел в виду изменение, примененное функцией. Но здесь я изменю это в ответе.
Анте Яблан Адамович

опять же неправильно, карта не изменяет и не изменяет все элементы в исходном массиве, я предложил и отредактирую ответ, подчеркнув, что карта работает с копией оригинальных элементов, оставив исходный массив без изменений
Jesús Franco

7

Вы можете позвонить forEach, как это:

forEachбудет выполнять итерацию по предоставленному вами массиву, и для каждой итерации будет иметь elementзначение этой итерации. Если вам нужен индекс, вы можете получить текущий индекс, передав в iкачестве второго параметра функцию обратного вызова для forEach.

Foreach - это в основном функция высокого порядка, которая принимает в качестве параметра другую функцию.

let theArray= [1,3,2];

theArray.forEach((element) => {
  // Use the element of the array
  console.log(element)
}

Вывод:

1
3
2

Вы также можете перебрать массив так:

for (let i=0; i<theArray.length; i++) {
  console.log(i); // i will have the value of each index
}

6

Если вы хотите перебрать массив объектов с помощью функции стрелки:

let arr = [{name:'john', age:50}, {name:'clark', age:19}, {name:'mohan', age:26}];

arr.forEach((person)=>{
  console.log('I am ' + person.name + ' and I am ' + person.age + ' old');
})


6

Лямбда-синтаксис обычно не работает в Internet Explorer 10 или ниже.

Я обычно использую

[].forEach.call(arrayName,function(value,index){
    console.log("value of the looped element" + value);
    console.log("index of the looped element" + index);
});

Если вы являетесь поклонником jQuery и у вас уже запущен файл jQuery, вам следует поменять местами позиции индекса и значения параметров

$("#ul>li").each(function(**index, value**){
    console.log("value of the looped element" + value);
    console.log("index of the looped element" + index);
});

5

Если у вас есть массивный массив, вы должны использовать, iteratorsчтобы получить некоторую эффективность. Итераторы свойство некоторых коллекций JavaScript (как Map, Set, String, Array). Даже for..ofиспользует iteratorпод капотом.

Итераторы повышают эффективность, позволяя вам использовать элементы списка по одному, как если бы они были потоком. Что делает итератор особенным, так это то, как он пересекает коллекцию. Другие циклы должны загружать всю коллекцию заранее, чтобы выполнить итерацию, тогда как итератору нужно знать только текущую позицию в коллекции.

Вы получаете доступ к текущему элементу, вызывая метод итератора next. Следующий метод вернет valueтекущий элемент и a, booleanчтобы указать, когда вы достигли конца коллекции. Ниже приведен пример создания итератора из массива.

Преобразуйте ваш обычный массив в итератор, используя values()метод, подобный этому:

    const myArr = [2,3,4]

let it = myArr.values();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

Вы также можете преобразовать свой обычный массив в итератор, используя Symbol.iteratorэто:

const myArr = [2,3,4]

let it = myArr[Symbol.iterator]();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

Вы также можете преобразовать свой обычный arrayв iteratorподобное:

let myArr = [8, 10, 12];

function makeIterator(array) {
    var nextIndex = 0;
    
    return {
       next: function() {
           return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    };
};

var it = makeIterator(myArr);

console.log(it.next().value);   // {value: 8, done: false}
console.log(it.next().value);   // {value: 10, done: false}
console.log(it.next().value);   // {value: 12, done: false}
console.log(it.next().value);   // {value: undefined, done: true}

ПРИМЕЧАНИЕ :

  • Итераторы носят исчерпывающий характер.
  • Объекты не iterableпо умолчанию. Используйте for..inв этом случае, потому что вместо значений он работает с ключами.

Вы можете прочитать больше о iteration protocol здесь .


5

Если вы хотите использовать forEach(), это будет выглядеть так -

theArray.forEach ( element => {
    console.log(element);
});

Если вы хотите использовать for(), это будет выглядеть так -

for(let idx = 0; idx < theArray.length; idx++){
    let element = theArray[idx];
    console.log(element);
}

4

Согласно новой обновленной функции ECMAScript 6 (ES6) и ECMAScript 2015, вы можете использовать следующие параметры с циклами:

для петель

for(var i = 0; i < 5; i++){
  console.log(i);
}

// Output: 0,1,2,3,4

для ... в петлях

let obj = {"a":1, "b":2}

for(let k in obj){
  console.log(k)
}

// Output: a,b

Array.forEach ()

let array = [1,2,3,4]

array.forEach((x) => {
  console.log(x);
})

// Output: 1,2,3,4

для ... петель

let array = [1,2,3,4]

for(let x of array){
  console.log(x);
}

// Output: 1,2,3,4

в то время как петли

let x = 0

while(x < 5){
  console.log(x)
  x++
}

// Output: 1,2,3,4

делать ... пока петли

let x = 0

do{
  console.log(x)
  x++
}while(x < 5)

// Output: 1,2,3,4

4

Представление

Сегодня (2019-12-18) я выполнить тест на моем Macos v10.13.6 (High Sierra), на Chrome против 79,0, Safari v13.0.4 и Firefox v71.0 (64 бит) - выводы об оптимизации (и микро-оптимизации , которые обычно не стоит вводить его в код, потому что выгода невелика, но сложность кода растет).

  • Похоже, традиционный for i( Aa ) - хороший выбор для написания быстрого кода во всех браузерах.

  • Другие решения, такие как for-of( Ad ), все в группе C. ... обычно в 2 - 10 (и более) раз медленнее, чем Aa , но для небольших массивов его можно использовать - для большей ясности кода.

  • Циклы с длиной массива, кэшированные в n( Ab, Bb, Be ), иногда быстрее, иногда нет. Вероятно, компиляторы автоматически обнаруживают эту ситуацию и вводят кэширование. Разница в скорости между кешированной и не кешированной версиями ( Aa, Ba, Bd ) составляет около ~ 1%, поэтому, похоже, введение nпредставляет собой микрооптимизацию .

  • i--, Как растворы , где цикл начинается с последнего элемента массива ( Ac, Bc ), как правило , ~ 30% медленнее , чем вперед решений - вероятно, причина , это путь памяти процессора кэш работы - вперед чтения памяти является более оптимальным для кэширования процессора). Рекомендуется НЕ ИСПОЛЬЗОВАТЬ такие решения.

подробности

В тестах мы вычисляем сумму элементов массива. Я выполняю тест для маленьких массивов (10 элементов) и больших массивов (1M элементов) и делю их на три группы:

  • А - forтесты
  • Б - whileтесты
  • C - другие / альтернативные методы

Результаты кросс-браузер

Результаты для всех протестированных браузеров

Введите описание изображения здесьбраузеры **

Массив с 10 элементами

Результаты для Chrome. Вы можете выполнить тест на вашей машине здесь .

Введите описание изображения здесь

Массив с 1 000 000 элементов

Результаты для Chrome. Вы можете выполнить тест на вашей машине здесь

Введите описание изображения здесь


4

Резюме:

При переборе массива мы часто хотим выполнить одну из следующих целей:

  1. Мы хотим перебрать массив и создать новый массив:

    Array.prototype.map

  2. Мы хотим перебрать массив и не создавать новый массив:

    Array.prototype.forEach

    for..of петля

В JavaScript есть много способов достижения обеих этих целей. Однако некоторые из них более удобны, чем другие. Ниже вы можете найти некоторые часто используемые методы (наиболее удобный IMO) для выполнения итерации массива в JavaScript.

Создание нового массива: Map

map()это функция, Array.prototypeкоторая может преобразовать каждый элемент массива, а затем вернуть новый массив. map()принимает в качестве аргумента функцию обратного вызова и работает следующим образом:

let arr = [1, 2, 3, 4, 5];

let newArr = arr.map((element, index, array) => {
  return element * 2;
})

console.log(arr);
console.log(newArr);

Обратный вызов, который мы передали в map()качестве аргумента, выполняется для каждого элемента. Затем возвращается массив, который имеет ту же длину, что и исходный массив. В этом новом элементе массива преобразовывается функция обратного вызова, переданная в качестве аргумента map().

Различия между mapдругим циклическим механизмом, подобным циклу, forEachи for..ofциклом, который mapвозвращает новый массив и оставляет старый массив без изменений (за исключением случаев, когда вы явно манипулируете им с помощью мышления splice).

Также обратите внимание, что mapобратный вызов функции предоставляет порядковый номер текущей итерации в качестве второго аргумента. Кроме того, третий аргумент предоставляет массив, на котором mapбыл вызван? Иногда эти свойства могут быть очень полезными.

Использование петли forEach

forEachявляется функцией, которая находится в функции, которая Array.prototypeпринимает функцию обратного вызова в качестве аргумента. Затем он выполняет эту функцию обратного вызова для каждого элемента в массиве. В отличие от map()функции, функция forEach ничего не возвращает ( undefined). Например:

let arr = [1, 2, 3, 4, 5];

arr.forEach((element, index, array) => {

  console.log(element * 2);

  if (index === 4) {
    console.log(array)
  }
  // index, and oldArray are provided as 2nd and 3th argument by the callback

})

console.log(arr);

Как и mapфункция, forEachобратный вызов предоставляет порядковый номер текущей итерации в качестве второго аргумента. Кроме того, третий аргумент предоставляет массив, на котором forEachбыл вызван?

Перебирать элементы, используя for..of

for..ofПетли петли через каждый элемент массива (или любого другого объекта Iterable). Это работает следующим образом:

let arr = [1, 2, 3, 4, 5];

for(let element of arr) {
  console.log(element * 2);
}

В приведенном выше примере elementобозначает элемент массива и arrявляется массивом, который мы хотим зациклить. Обратите внимание, что имя elementявляется произвольным, и мы могли бы выбрать любое другое имя, например «el», или что-то более декларативное, когда это применимо.

Не путайте for..inпетлю с for..ofпетлей. for..inбудет проходить через все перечисляемые свойства массива, тогда как for..ofцикл будет проходить только через элементы массива. Например:

let arr = [1, 2, 3, 4, 5];

arr.foo = 'foo';

for(let element of arr) {
  console.log(element);
}

for(let element in arr) {
  console.log(element);
}

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.