Для каждого над массивом в JavaScript? - PullRequest
4277 голосов
/ 17 февраля 2012

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

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

forEach(instance in theArray)

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

Ответы [ 33 ]

6603 голосов
/ 17 февраля 2012

TL; DR

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

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

Но есть лотов больше для изучения, читайте дальше ...


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

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

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

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

У вас есть три опции в 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 на веб-странице общего назначения без прокладки. Если вам требуется поддержка устаревших браузеров, легко выполнить shimming / polyfilling forEach (найдите «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"

let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
    let value = a[index];
    console.log(index, value);
}
try {
    console.log(index);
} catch (e) {
    console.error(e);   // "ReferenceError: index is not defined"
}
try {
    console.log(value);
} catch (e) {
    console.error(e);   // "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);
    });
}

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

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

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

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

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

Рассматривая только первый пример: вы можете использовать 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).Откуда этот номер?Это часть определения индекса массива <a href="https://tc39.github.io/ecma262/#array-index" rel="noreferrer"> в спецификации .Другие числа (нецелые числа, отрицательные числа, числа больше 2 ^ 32 - 2) не являются индексами массива.Причина, по которой он равен 2 ^ 32 - 2 , заключается в том, что наибольшее значение индекса становится на единицу меньше, чем 2 ^ 32 - 1 , что является максимальным значением, которое может иметь массив length.(Например, длина массива соответствует 32-разрядному целому числу без знака.) (Попросил RobG указать в комментарии к моему сообщению в блоге , что мой предыдущий тест был не совсем верным.)

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

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

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

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

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

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

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

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

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] для этой итерации.

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

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

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

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

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

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

    Предположим, вы хотите использовать forEach для свойства Node * 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 будет использовать итератор, предоставленный объектом (если есть);мы должны увидеть, как это работает с различными объектами, похожими на массивы, особенно с хост-объектами.Например, спецификация для NodeList из querySelectorAll была обновлена ​​для поддержки итерации.Спецификации для HTMLCollection из getElementsByTagName не было.

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

    См. № 4, мыПосмотрим, как будут работать итераторы.

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

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

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

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

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

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

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

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

  2. Use Синтаксис распространения (...)

    Также возможно использовать ES2015 синтаксис распространения с механизмами JavaScript, поддерживающими эту функцию:

    var trueArray = [...iterableObject];
    

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

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

    Array.from (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]], но это важно проверить.)

484 голосов
/ 17 февраля 2012

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

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

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

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

221 голосов
/ 17 февраля 2012

Если вы используете библиотеку 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].
}
98 голосов
/ 02 мая 2014

Цикл назад

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

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, просто выполните цикл вперед и добавьте как обычно (а также используйте «фрагмент документа»).
  • Обратный цикл - это сбивает с толку для начинающих разработчиков.(Вы можете считать это преимуществом, в зависимости от вашего мировоззрения.)

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

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

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

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

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

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

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

В общем случае для более высокого уровнякод уровня, где ясность и безопасность являются более серьезными проблемами, я бы рекомендовал использовать Array::forEach в качестве шаблона по умолчанию:

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

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

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


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

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]

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

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


Пустяки

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

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

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

77 голосов
/ 17 февраля 2012

Некоторые языки стиля 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 для обеспечения замкнутых контуров.

37 голосов
/ 17 февраля 2012

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

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

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

29 голосов
/ 21 июня 2014

Я знаю, что это старый пост, и уже есть так много хороших ответов.Для большей полноты я решил добавить еще один, используя 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.

Плюсы

  • Легко читаемость
  • Легко записываемо
  • Если доступно, angular.forEach будет использовать цикл ESE 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
        }
    }
}

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

Минусы

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

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

28 голосов
/ 10 апреля 2013

A 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
  }
);
28 голосов
/ 10 марта 2013

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

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

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

25 голосов
/ 25 декабря 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
...