For-each по массиву в JavaScript

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

Я думал, что это примерно так:

forEach(instance in theArray)

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


person Dante1986    schedule 17.02.2012    source источник
comment
ответы на этот вопрос обсуждаются на сайте meta   -  person gnat    schedule 07.11.2020


Ответы (39)


TL; DR

  • Ваши лучшие ставки обычно

    • a for-of loop (ES2015+ only; spec | MDN) - simple and async-friendly
    • forEach (только ES5 +; spec | MDN) (или его родственники some и подобные) - не async -дружелюбный (но см. подробности)
    • простой старомодный for цикл - async дружественный
    • (редко) for-in с защитой - asyncдружественный
  • Несколько быстрых запретов:

    • Don't use for-in unless you use it with safeguards or are at least aware of why it might bite you.
    • Не используйте map, если вы не используете его возвращаемое значение.
      (К сожалению, там кто-то преподает map [spec / MDN], как если бы это было forEach, но это не то, для чего он нужен. Если вы не используете массив, который он создает, не используйте map.)
    • Не используйте forEach, если обратный вызов выполняет асинхронную работу и вы хотите, чтобы forEach ждал, пока эта работа не будет выполнена (потому что этого не произойдет).

Но есть многое, что нужно изучить, читайте дальше ...


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

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

Для реальных массивов

У вас есть пять вариантов (два поддерживаются практически навсегда, еще один добавлен в ECMAScript 5 [ES5], и еще два добавлены в ECMAScript 2015 (ES2015, он же ES6):

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

(Вы можете увидеть эти старые спецификации здесь: ES5, ES2015, но оба были заменены; текущий черновик редактора всегда здесь.)

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

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

ES2015 добавил итераторы и итерации в JavaScript. Массивы являются итеративными (как и строки, Maps и Sets, а также коллекции и списки DOM, как вы увидите позже). Итерируемые объекты предоставляют итераторы для своих значений. Новый оператор for-of просматривает значения, возвращаемые итератором:

const a = ["a", "b", "c"];
for (const val of a) { // You can use `let` instead of `const` if you like
    console.log(val);
}
// a
// b
// c

Нет ничего проще! Под покровом, который получает итератор из массива и перебирает значения, возвращаемые итератором. Итератор, предоставляемый массивами, предоставляет значения элементов массива в порядке от начала до конца.

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

Теоретически цикл for-of включает несколько вызовов функций (один для получения итератора, затем один для получения каждого значения из него). Даже если это правда, беспокоиться не о чем, вызовы функций очень дешевы в современных движках JavaScript (меня это беспокоило forEach [ниже], пока я не изучил; подробности). Но, кроме того, движки JavaScript оптимизируют эти вызовы (в критически важном для производительности коде) при работе с собственными итераторами для таких вещей, как массивы.

for-of полностью async дружелюбен. Если вам нужно, чтобы работа в теле цикла выполнялась последовательно (не параллельно), await в теле цикла будет ждать выполнения обещания, прежде чем продолжить. Вот глупый пример:

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function showSlowly(messages) {
    for (const str of messages) {
        await delay(400);
        console.log(str);
    }
}

showSlowly([
    "So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects

Обратите внимание на то, как слова появляются с задержкой перед каждым словом.

Это вопрос стиля кодирования, но for-of - это первое, чего я добиваюсь при циклическом прохождении чего-либо итерируемого.

2. Используйте forEach и похожие

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

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

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

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

В отличие от for-of, forEach имеет недостаток в том, что он не понимает async функций и await. Если вы используете async функцию в качестве обратного вызова, forEach не ожидает выполнения обещания этой функции, прежде чем продолжить. Вот пример async из for-of, использующий вместо этого forEach - обратите внимание, что есть начальная задержка, но затем весь текст появляется сразу, а не ждет:

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function showSlowly(messages) {
    messages.forEach(async message => { // Doesn't wait before continuing
        await delay(400);
        console.log(message);
    });
}

showSlowly([
    "So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects

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

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

Как и в случае с forEach, если вы используете функцию async в качестве обратного вызова, никто из них не ожидает выполнения обещания функции. Это означает:

  • Использование обратного вызова функции async никогда не подходит для every, some и filter, поскольку они будут рассматривать возвращенное обещание, как если бы оно было истинным значением; они не ждут, пока обещание исполнится, а затем используют ценность выполнения.
  • Использование обратного вызова функции async часто уместно с map, если цель состоит в том, чтобы превратить массив чего-либо в массив обещаний, возможно, для передачи одному из комбинаторов обещаний. функции (Promise.all, _ 71_, _ 72_ или _ 73_).
  • Использование обратного вызова функции async редко уместно с reduce или reduceRight, потому что (опять же) обратный вызов всегда будет возвращать обещание. Но есть идиома построения цепочки обещаний из массива, который использует reduce (const promise = array.reduce((p, element) => p.then(/*...something using `element`...*/));), но обычно в таких случаях цикл for-of или for в функции async будет более понятным и легче поддается отладке.

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

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

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

Если длина массива не изменится во время цикла и он находится в чувствительном к производительности коде (что маловероятно), чуть более сложная версия с захватом длины впереди может быть tiny < / em> немного быстрее:

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

И / или обратный отсчет:

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

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

До ES2015 переменная цикла должна была существовать в содержащей области, потому что var имеет только область видимости на уровне функции, а не на уровне блока. Но, как вы видели в приведенных выше примерах, вы можете использовать let в for, чтобы ограничить переменные только циклом. И когда вы это сделаете, переменная index воссоздается для каждой итерации цикла, что означает, что замыкания, созданные в теле цикла, сохраняют ссылку на index для этой конкретной итерации, что решает проблему старых замыканий в циклах:

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

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

Подобно for-of, for циклы хорошо работают в async функциях. Вот предыдущий пример с использованием цикла for:

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function showSlowly(messages) {
    for (let i = 0; i < messages.length; ++i) {
        await delay(400);
        console.log(messages[i]);
    }
}

showSlowly([
    "So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects

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

for-in не для циклического перебора массивов, а для перебора имен свойств объекта. Кажется, что это часто работает для циклического перебора массивов как побочного продукта того факта, что массивы являются объектами, но он не просто перебирает индексы массива, он перебирает все перечислимые свойства объекта объект (в том числе унаследованный). (Раньше также считалось, что заказ не был указан; теперь это [подробности в этом другом ответе], но Несмотря на то, что порядок указан сейчас, правила сложны, есть исключения, и полагаться на порядок - не лучшая практика.)

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

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

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

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

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

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

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

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

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

// Utility function for antiquated environments without `forEach`
const hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
const rexNum = /^0$|^[1-9]\d*$/;
function sparseEach(array, callback, thisArg) {
    for (const name in array) {
        const index = +name;
        if (hasOwn(a, name) &&
            rexNum.test(name) &&
            index <= 4294967294
           ) {
            callback.call(thisArg, array[name], index, array);
        }
    }
}

const a = [];
a[5] = "five";
a[10] = "ten";
a[100000] = "one hundred thousand";
a.b = "bee";

sparseEach(a, (value, index) => {
    console.log("Value at " + index + " is " + value);
});

... хотя с учетом сказанного, большая часть кода выполняет только проверку hasOwnProperty.

Как и for, for-in хорошо работает в асинхронных функциях, если работа в нем должна выполняться последовательно.

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function showSlowly(messages) {
    for (const name in messages) {
        if (messages.hasOwnProperty(name)) {
            await delay(400);
            console.log(messages[name]);
        }
    }
}

showSlowly([
    "So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects

5. Явное использование итератора (ES2015 +)

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

const a = ["a", "b", "c"];
const it = a.values(); // Or `const it = a[Symbol.iterator]();` if you like
let entry;
while (!(entry = it.next()).done) {
    console.log(entry.value);
}

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

То, что вы получите за value, зависит от итератора. В массивах итератор по умолчанию предоставляет значение каждого элемента массива ("a", "b" и "c" в примере ранее). У массивов также есть три других метода, возвращающих итераторы:

  • values(): это псевдоним для метода [Symbol.iterator], который возвращает итератор по умолчанию.
  • keys(): возвращает итератор, который предоставляет каждый ключ (индекс) в массиве. В приведенном выше примере он предоставит "0", затем "1", затем "2" (да, в виде строк).
  • entries(): возвращает итератор, который предоставляет [key, value] массивы.

Поскольку объекты-итераторы не продвигаются вперед, пока вы не вызовете next, они хорошо работают в async циклах функций. Вот предыдущий пример for-of с явным использованием итератора:

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function showSlowly(messages) {
    const it = messages.values()
    while (!(entry = it.next()).done) {
        await delay(400);
        console.log(entry.value);
    }
}

showSlowly([
    "So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects

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

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

Используйте большинство из вышеперечисленных опций

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

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

    for-of использует итератор, предоставленный объектом (если есть ). Сюда входят объекты, предоставляемые хостом (например, коллекции и списки DOM). Например, HTMLCollection экземпляры из getElementsByXYZ методов и NodeLists экземпляры из querySelectorAll поддерживают итерацию. (Это определяется довольно тонко в спецификациях HTML и DOM. Как правило, любой объект с length и индексированным доступом является автоматически повторяемым. Он не должен быть помечен iterable; который используется только для коллекций, которые, помимо итерируемости, поддерживают методы forEach, values, keys и entries. NodeList поддерживает; HTMLCollection нет, но оба являются итеративными.)

    Вот пример перебора div элементов:

const divs = document.querySelectorAll("div");
for (const div of divs) {
    div.textContent = Math.random();
}
<div>zero</div>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>

  1. Используйте forEach и похожие (ES5 +)

    Различные функции на Array.prototype намеренно являются общими и могут использоваться с объектами, подобными массивам, через Function#call (spec | MDN ) или Function#apply (spec | MDN). (Если вам приходится иметь дело с IE8 или более ранней версией [ай], см. Предостережение для объектов, предоставляемых хостом, в конце этого ответа, но это не проблема с неопределенно современными браузерами.)

    Предположим, вы хотите использовать forEach в коллекции childNodes Node (которая, будучи HTMLCollection, изначально не содержит forEach). Вы бы сделали это:

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

    (Обратите внимание, что вы можете просто использовать for-of на node.childNodes.)

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

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

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

  3. Явное использование итератора (ES2015 +)

    См. №1.

Вы можете обойтись for-in (с мерами предосторожности), но со всеми этими более подходящими вариантами нет причин пытаться.

Создайте настоящий массив

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

  1. Используйте Array.from

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

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

    ... берет NodeList из querySelectorAll и создает из него массив.

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

    // Typical use (with an arrow function):
    const divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
    
    // Traditional function (since `Array.from` can be polyfilled):
    var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {
        return element.tagName;
    });
    
  2. Используйте синтаксис распространения (...)

    Также можно использовать синтаксис распространения ES2015. Как и for-of, здесь используется итератор, предоставляемый объектом. (см. №1 в предыдущем разделе):

    const trueArray = [...iterableObject];
    

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

    const divs = [...document.querySelectorAll("div")];
    
  3. Используйте slice метод массивов

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

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

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

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

    (Если вам все еще нужно обрабатывать IE8 [ай], произойдет сбой; IE8 не позволял вам использовать объекты, предоставляемые хостом, как this вот так.)

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

Если вы используете Array.prototype функции с предоставляемыми хостом объектами, подобными массивам (например, коллекции DOM и тому подобное, предоставляемые браузером, а не движком JavaScript), устаревшие браузеры, такие как IE8, не обязательно справляются с этим. , поэтому, если вам нужно их поддерживать, обязательно протестируйте в своих целевых средах. Но это не проблема современных браузеров. (Для сред без браузера, естественно, это будет зависеть от среды.)

person T.J. Crowder    schedule 17.02.2012
comment
Что вы имеете в виду под неэлементными свойствами? - person Aalexander; 09.12.2020
comment
@Alex - свойства массива, которые не представляют элементы массива. Например: const a = ["a", "b"]; a.example = 42; Этот массив имеет три свойства (кроме тех, которые есть у всех массивов), имена которых - строки "0", "1" и "example". Свойство с именем "example" не является свойством элемента. Два других - это свойства элементов, поскольку они представляют элементы массива. - person T.J. Crowder; 09.12.2020

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

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

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

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-цикл также будет перечисляться в старых браузерах, или если дополнительные свойства определены как enumerable.

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

person PatrikAkerstrand    schedule 17.02.2012
comment
Есть ли способ сделать это одной строчкой кода. Например, в фейсбуке мне нравится ускорять видео с помощью document.getElementsByTagName("video")[28].playbackRate = 2.2. Если бы я мог легко сопоставить все элементы, мне бы не пришлось определять, какое видео (например, индекс 28 в этом случае). Любые идеи? - person stevec; 05.09.2020
comment
@stevec: Array.from (document.querySelectorAll ('video')). forEach (video = ›video.playbackRate = 2.2); - person PatrikAkerstrand; 14.09.2020

Если вы используете библиотеку 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].
}
person Poonam    schedule 17.02.2012

Петля назад

Я думаю, что здесь стоит упомянуть цикл reverse for:

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

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

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

Недостатки:

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

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

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

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

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

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

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

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

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

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

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

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

Теперь даже лучше использовать for..of!

Ведутся споры о том, что предпочтительнее: for..of или forEach():

  • Для максимальной поддержки браузера for..of требуется полифилл для итераторов, что делает ваше приложение немного медленнее для выполнения и немного больше для загрузки.

  • По этой причине (и для поощрения использования map и filter) некоторые руководства по стилю интерфейса забанить for..of полностью!

  • Но вышеуказанные проблемы не применимы к приложениям Node.js, где for..of теперь хорошо поддерживается.

  • Кроме того, await не работает внутри forEach(). Использование for..of - это самый ясный шаблон в этом случае.

Лично я предпочитаю использовать то, что кажется наиболее легким для чтения, если только производительность или минимизация не стали серьезной проблемой. Поэтому сейчас я предпочитаю использовать for..of вместо forEach(), но всегда буду использовать map, filter или _ 27_ или _ 28_, если применимо. (Ради своих коллег я редко использую 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, что позволяет избежать каких-либо проблем с элементами за пределами массива undefined.

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

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

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

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

    [5, 5, 4]

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

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


Мелочи

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

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

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

person joeytwiddle    schedule 02.05.2014

Некоторые языки в стиле 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 добавил _ 6_ к Array.prototype, который можно использовать для перечисления по массиву с помощью обратного вызова (полифилл находится в документации так что вы все еще можете использовать его для старых браузеров):

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

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

person zzzzBov    schedule 17.02.2012

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

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

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

person Quentin    schedule 17.02.2012

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

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

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

person gaby de wilde    schedule 10.03.2013

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

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

Есть разные способы использования цикла 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.

Плюсы

  • Легкая читаемость
  • Легкая запись
  • Если возможно, angular.forEach будет использовать цикл forEach ES5. Теперь я перейду к эффективности в разделе cons, поскольку циклы 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
        }
    }
}

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

Минусы

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

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

person user2359695    schedule 20.06.2014

Реализация 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
  }
);
person nmoliveira    schedule 10.04.2013

Начиная с ECMAScript 6:

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

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

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

person Zaz    schedule 26.05.2016

Возможно, петля 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
//!!!
person Federico Piragua    schedule 02.11.2013

Ниже приведены три реализации 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
person Rajesh Paul    schedule 25.12.2013

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

Пример работы CodePen:

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

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

person Micka    schedule 17.07.2013

В собственном JavaScript нет for each цикла. Вы можете использовать библиотеки, чтобы получить эту функциональность (я рекомендую 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];
    ...
}
person joidegn    schedule 17.02.2012

Это итератор для НЕ разреженного списка, где индекс начинается с 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');

Каждый тег p получает 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 меняются на зеленые.

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

person Tim    schedule 30.01.2014

Есть несколько способов перебрать массив в 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: отображение, фильтрация и сокращение.

person Alireza    schedule 10.05.2017

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

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

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

// Output: false

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

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

// Output: [2,4,6]
person Anil Kumar Arya    schedule 09.04.2017

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

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

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

person Priyanshu Chauhan    schedule 27.07.2015

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

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

Плюсы:

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

Минусы:

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

person Volkan Seçkin Akbayır    schedule 28.08.2015

Способ 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];
person Daniel W.    schedule 01.04.2014

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

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

В следующих примерах будет использоваться _1 _ и метод .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);
});

person darklightcode    schedule 09.10.2018

Наиболее близким к вашей идее будет использование 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]
person Ante Jablan Adamović    schedule 09.11.2017

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

Сегодня (18 декабря 2019 г.) я провожу тестирование на своем macOS v10.13.6 (High Sierra ), в Chrome v 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 - это micro-optimisation.

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

Подробности

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

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

let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
//let arr = Array.from(Array(1000000), (x, i) => i%10);

function Aa(a, s=0) {
  for(let i=0; i<a.length; i++) {
    s += a[i];
  }
  console.log('Aa=', s);
}

function Ab(a, s=0) {
  let n = a.length;
  for(let i=0; i<n; i++) {
    s += a[i];
  }
  console.log('Ab=', s);
}

function Ac(a, s=0) {
  for(let i=a.length; i--;) {
    s += a[i];
  }
  console.log('Ac=', s);
}

function Ad(a, s=0) {
  for(let x of a) {
    s += x;
  }
  console.log('Ad=', s);
}

function Ae(a, s=0) {
  for(let i in a) if (a.hasOwnProperty(i)) {
    s += a[i];
  }
  console.log('Ae=', s);
}

function Ba(a, s=0) {
  let i = -1;
  while(++i < a.length) {
    s+= a[i];
  }
  console.log('Ba=', s);
}

function Bb(a, s=0) {
  let i = -1;
  let n = a.length;
  while(++i < n) {
    s+= a[i];
  }
  console.log('Bb=', s);
}

function Bc(a, s=0) {
  let i = a.length;
  while(i--) {
    s += a[i];
  }
  console.log('Bc=', s);
}

function Bd(a, s=0) {
  let i = 0;
  do {
    s+= a[i]
  } while (++i < a.length);
  console.log('Bd=', s);
}

function Be(a, s=0) {
  let i = 0;
  let n = a.length;
  do {
    s += a[i]
  } while (++i < n);
  console.log('Be=', s);
}

function Bf(a, s=0) {
  const it = a.values(); 
  let e;
  while (!(e = it.next()).done) { 
    s+= e.value; 
  }
  console.log('Bf=', s);
}

function Ca(a, s=0) {
  a.map(x => { s+=x });
  console.log('Ca=', s);
}

function Cb(a, s=0) {
  a.forEach(x => { s+=x });
  console.log('Cb=', s);
}

function Cc(a, s=0) {
  a.every(x => (s += x, 1));
  console.log('Cc=', s);
}

function Cd(a, s=0) {
  a.filter(x => { s+=x });
  console.log('Cd=',s);
}

function Ce(a, s=0) {
  a.reduce((z, c) => { s+=c }, 0);
  console.log('Ce=', s);
}

function Cf(a, s=0) {
  a.reduceRight((z, c) => { s += c }, 0);
  console.log('Cf=', s);
}

function Cg(a, s=0) {
  a.some(x => { s += x } );
  console.log('Cg=', s);
}

function Ch(a, s=0) {
  Array.from(a, x=> s += x);
  console.log('Cc=', s);
}


Aa(arr);
Ab(arr);
Ac(arr);
Ad(arr);
Ae(arr);

Ba(arr);
Bb(arr);
Bc(arr);
Bd(arr);
Be(arr);
Bf(arr);

Ca(arr);
Cb(arr);
Cc(arr);
Cd(arr);
Ce(arr);
Cf(arr);
Cg(arr);
Ch(arr);
<p style="color: red">This snippets only PRESENTS code used for benchmark - it not perform test itself</p>

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

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

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

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

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

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

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

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

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

person Kamil Kiełczewski    schedule 18.12.2019

Лямбда-синтаксис обычно не работает в 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);
});
person Murtuza Husain    schedule 11.11.2017

Вы можете вызвать 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
}
person Nouman Dilshad    schedule 17.07.2018

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

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

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

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

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

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

person subhashish negi    schedule 08.03.2019

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

для петель

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

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

for ... в циклах

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

циклы while

let x = 0

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

// Output: 1,2,3,4

do ... while циклы

let x = 0

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

// Output: 1,2,3,4
person ankitkanojia    schedule 22.01.2020

Резюме:

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

  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 возвращает новый массив и оставляет старый массив нетронутым (кроме случаев, когда вы явно манипулируете им с помощью Thinks like 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 проходит через каждый элемент массива (или любого другого итерируемого объекта). Это работает следующим образом:

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

person Willem van der Veen    schedule 08.09.2018

Если у вас массивный массив, вы должны использовать _1 _ , чтобы повысить эффективность. Итераторы являются свойством определенных коллекций JavaScript (например, _2 _, Set, _ 4_, _ 5_). Даже _6 _ использует iterator под капотом.

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

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

Преобразуйте обычный массив в итератор, используя _11 _ метод вроде этого:

    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 здесь.

person BlackBeard    schedule 06.07.2018

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

  1. Для каждого

    theArray.forEach(function (array, index) {
        console.log(index);
        console.log(array);
    });
    
  2. для

    for(var i=0; i<theArray.length; i++) {
        console.log(i)
    }
    
  3. карта

    theArray.map(x => console.log(x));
    
  4. карта

    theArray.filter(x => console.log(x));
    

И есть много других для повторения.

person Mustafa    schedule 19.12.2019

Я бы сказал, что for/of - это правильный путь:

const arr = ['a', 'b', 'c'];

for (const v of arr) {
  console.log(v); // Prints "a", "b", "c"
}

  • В отличие от for/in, for/of пропускает нечисловые свойства в массиве. Например, если вы установите arr.foo = 'test', for (var v in arr) будет проходить через клавишу 'foo'.

  • В отличие от forEach(), for/of не пропускает «дыр» в массивах. const arr = ['a',, 'c'] - это допустимый JavaScript, только второй элемент - это «дыра». Массив функционально эквивалентен ['a', undefined, 'c'].

Дополнительную информацию можно найти в этом сообщение в блоге о for/of vs forEach().

person vkarpov15    schedule 23.12.2019

Предположим, у нас есть набор предметов:

  let ddl = new Array();
    if (subjects) {
      subjects.forEach(function (s) {ddl.push({"id": s.id,"label": s.name});});
    }
person Muhammad Waqas    schedule 28.03.2020

Если вы хотите сохранить функциональность своего кода, используйте map:

theArray.map(instance => do_something);

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

person alejoko    schedule 02.02.2019

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

См. Массивы в JavaScript: часть 2 .

person Ayush Jain    schedule 11.03.2019

Я пришел из Python, и этот способ мне показался более понятным.

theArray - это массив, а instance - элементы массива:

for (let instance of theArray)
{
    console.log("The instance", instance);
}

or

for (instance in theArray)
{
    console.log("The instance", instance);
}

сравнить с:

theArray.forEach(function(instance) {
    console.log(instance);
});

Но в конце концов оба делают одно и то же.

person Peko Chan    schedule 12.12.2018
comment
Он возвращает индекс, а не экземпляры объекта - person Rusty; 03.07.2019

Документация Mozilla

/* Get all forms */
document.querySelectorAll( "form" ).forEach( form => {

  /* For each form, add the onsubmit event */
  form.addEventListener( "submit", event => {
    event.preventDefault(); // Return false

    /* Display it */
    alert(event.target.action);
    console.log(event.target);
  } );

} );
<form action="form1.php" >
  <input type="submit" value="Submit" />
</form>
<form action="form2.php" >
  <input type="submit" value="Submit" />
</form>
<form action="form3.php" >
  <input type="submit" value="Submit" />
</form>

person antelove    schedule 14.12.2019

Используя функцию grep в jQuery, например:

$.grep([0, 1, 2], function(n, i) {
    return n > 0;
});
person mojtaba ramezani    schedule 27.01.2020

person    schedule
comment
Это ничего не добавляет. - person Jasper; 12.01.2018