JavaScript - Нюансы myArray.forEach против цикла
Я видел много вопросов, которые предлагают использовать:
for (var i = 0; i < myArray.length; i++){ /* ... */ }
вместо:
for (var i in myArray){ /* ... */ }
для массивов из-за несогласованной итерации (см. здесь).
Однако я не могу найти ничего похожего на объектно-ориентированный цикл:
myArray.forEach(function(item, index){ /* ... */ });
Что мне кажется более интуитивным.
Для моего текущего проекта важна совместимость IE8, и я рассматриваю возможность использования Mozilla polyfill, однако я не на 100% как это будет работать.
- Существуют ли различия между стандартом цикла (первый пример выше) и реализацией Array.prototype.forEach современными браузерами?
- Есть ли разница между современными реализациями браузеров и реализацией Mozilla, связанными с выше (с особым учетом IE8)?
- Производительность - это не такая уж большая проблема, просто согласованность с тем, какие свойства повторяются.
Ответы
Ответ 1
Самое существенное различие между циклом for
и методом forEach
заключается в том, что с первым вы можете break
выйти из цикла. Вы можете имитировать continue
, просто возвращаясь из функции, переданной в forEach
, но нет возможности полностью остановить цикл.
Кроме того, эти два эффективно выполняют те же функции. Другая незначительная разница связана с областью действия индекса (и всех содержащихся переменных) в цикле for из-за переменной подъема.
// 'i' is scoped to the containing function
for (var i = 0; i < arr.length; i++) { ... }
// 'i' is scoped to the internal function
arr.forEach(function (el, i) { ... });
Однако я нахожу, что forEach
гораздо более выразителен - он представляет ваше намерение итерации через каждый элемент массива и предоставляет вам ссылку на элемент, а не только на индекс. В целом, в основном это зависит от личного вкуса, но если вы можете использовать forEach
, я бы рекомендовал его использовать.
Есть несколько существенных различий между этими двумя версиями, особенно в отношении производительности. Фактически, простой цикл цикла работает значительно лучше, чем метод forEach
, как показано этот тест jsperf.
Независимо от того, нужна ли вам такая производительность, вам решать, и в большинстве случаев я бы предпочитал выразительность по скорости. Эта разница в скорости, вероятно, объясняется незначительными семантическими различиями между основным циклом и методом при работе на разреженных массивах, как указано в этом ответе.
Если вам не нужно поведение forEach
и/или вам нужно вырваться из цикла раньше, вы можете использовать Lo-Dash _.each
в качестве альтернативы, которая также будет работать в кросс-браузере. Если вы используете jQuery, он также предоставляет аналогичный $.each
, просто обратите внимание на различия в аргументах, переданных функции обратного вызова в каждом варианте.
(Что касается forEach
polyfill, он должен работать в старых браузерах без проблем, если вы решите пойти по этому маршруту.)
Ответ 2
Вы можете использовать свою пользовательскую функцию foreach, которая будет работать намного лучше, чем Array.forEach.
Вы должны добавить это один раз в свой код. Это добавит новую функцию в массив.
function foreach(fn) {
var arr = this;
var len = arr.length;
for(var i=0; i<len; ++i) {
fn(arr[i], i);
}
}
Object.defineProperty(Array.prototype, 'customForEach', {
enumerable: false,
value: foreach
});
Тогда вы можете использовать его в любом месте, как Array.forEach
[1,2,3].customForEach(function(val, i){
});
Единственная разница это в 3 раза быстрее. https://jsperf.com/native-arr-foreach-vs-custom-foreach
ОБНОВЛЕНИЕ: В новой версии Chrome улучшена производительность .forEach(). Однако решение может дать дополнительную производительность в других браузерах.
![JSPerf]()
Ответ 3
Многие разработчики (например, Кайл Симпсон) предлагают использовать .forEach
чтобы указать, что массив будет иметь побочный эффект и .map
для чистых функций. Циклы for
подходят как универсальное решение для известного числа циклов или любого другого случая, который не подходит, так как легче общаться из-за его широкой поддержки в большинстве языков программирования.
например
/* For Loop known number of iterations */
const numberOfSeasons = 4;
for (let i = 0; i < numberOfSeasons; i++) {
//Do Something
}
/* Pure transformation */
const arrayToBeUppercased = ['www', 'html', 'js', 'us'];
const acronyms = arrayToBeUppercased.map((el) => el.toUpperCase));
/* Impure, side-effects with .forEach */
const acronymsHolder = [];
['www', 'html', 'js', 'us'].forEach((el) => acronymsHolder.push(el.toUpperCase()));
С точки зрения соглашения это кажется лучшим, однако сообщество на самом деле не остановилось на соглашении о более новом протоколе итерации for in
циклов for in
. В целом, я считаю хорошей идеей следовать концепциям FP, которые сообщество JS, похоже, готово принять.