Как я могу перебрать все записи в массиве с помощью JavaScript?
Я думал, что это примерно так:
forEach(instance in theArray)
Где theArray
- мой массив, но это кажется неверным.
Как я могу перебрать все записи в массиве с помощью JavaScript?
Я думал, что это примерно так:
forEach(instance in theArray)
Где theArray
- мой массив, но это кажется неверным.
TL; DR
Ваши лучшие ставки обычно
Несколько быстрых запретов:
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):
for-of
(использовать итератор неявно) (ES2015 +)forEach
и похожие (ES5 +)for
циклfor-in
правильно(Вы можете увидеть эти старые спецификации здесь: ES5, ES2015, но оба были заменены; текущий черновик редактора всегда здесь.)
Подробности:
for-of
(используйте итератор неявно) (ES2015 +)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
- это первое, чего я добиваюсь при циклическом прохождении чего-либо итерируемого.
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
будет более понятным и легче поддается отладке.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
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]);
}
}
Обратите внимание на три проверки:
Что объект имеет свое собственное свойство с этим именем (а не то, которое он наследует от своего прототипа), и
Имя состоит только из десятичных цифр (например, в обычной строковой форме, а не в научном представлении), и
Что значение имени при принуждении к числу будет ‹= 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
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
экземпляров a >, объект arguments
и т. д. Как просмотреть их содержимое в цикле?
По крайней мере, некоторые, а, возможно, большая часть или даже все из вышеперечисленных подходов к массивам одинаково хорошо применимы к объектам, подобным массивам:
Использовать for-of
(использовать итератор неявно) (ES2015 +)
for-of
использует итератор, предоставленный объектом (если есть ). Сюда входят объекты, предоставляемые хостом (например, коллекции и списки DOM). Например, HTMLCollection
экземпляры из getElementsByXYZ
методов и NodeList
s экземпляры из 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>
Используйте 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`
});
Используйте простую for
петлю
Возможно, очевидно, что простой цикл for
работает для объектов, подобных массиву.
Явное использование итератора (ES2015 +)
См. №1.
Вы можете обойтись for-in
(с мерами предосторожности), но со всеми этими более подходящими вариантами нет причин пытаться.
В других случаях вы можете захотеть преобразовать объект, подобный массиву, в настоящий массив. Сделать это на удивление легко:
Используйте 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;
});
Используйте синтаксис распространения (...
)
Также можно использовать синтаксис распространения ES2015. Как и for-of
, здесь используется итератор, предоставляемый объектом. (см. №1 в предыдущем разделе):
const trueArray = [...iterableObject];
Так, например, если мы хотим преобразовать NodeList
в настоящий массив, с синтаксисом распространения это становится довольно лаконичным:
const divs = [...document.querySelectorAll("div")];
Используйте 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, не обязательно справляются с этим. , поэтому, если вам нужно их поддерживать, обязательно протестируйте в своих целевых средах. Но это не проблема современных браузеров. (Для сред без браузера, естественно, это будет зависеть от среды.)
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". Однако полифил для этой функции является тривиальным и, поскольку он упрощает чтение кода, это хороший полифилл для включения.
document.getElementsByTagName("video")[28].playbackRate = 2.2
. Если бы я мог легко сопоставить все элементы, мне бы не пришлось определять, какое видео (например, индекс 28 в этом случае). Любые идеи?
- person stevec; 05.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].
}
Я думаю, что здесь стоит упомянуть цикл reverse for:
for (var i = array.length; i--; ) {
// process array[i]
}
len
переменную или сравнивать с array.length
на каждой итерации, что может быть минутной оптимизацией.array[i]
), то прямой цикл пропустит элемент, который сдвинулся left в позицию i, или повторно обработать i -й элемент, который был сдвинут вправо. В традиционном цикле for вы можете обновить i так, чтобы он указывал на следующий элемент, который требует обработки - 1, но просто изменить направление итерации на обратное часто бывает проще и более элегантное решение .forEach()
и for ... of
от ES6.Некоторые разработчики используют обратный цикл for по умолчанию, если нет веской причины для перехода вперед.
Хотя прирост производительности обычно незначительный, он вроде кричит:
«Просто проделайте это с каждым элементом в списке, меня не волнует порядок!»
Однако на практике это не на самом деле является надежным признаком намерения, поскольку его невозможно отличить от тех случаев, когда вы действительно заботитесь о порядке, и действительно нужно использовать цикл в обратном порядке. Так что на самом деле потребуется другая конструкция для точного выражения намерения «безразлично», что в настоящее время недоступно в большинстве языков, включая ECMAScript, но которое можно было бы назвать, например, forEachUnordered()
.
Если порядок не имеет значения, а эффективность вызывает беспокойство (в самом внутреннем цикле игры или движка анимации), то может быть приемлемым использование обратного цикла for в качестве шаблона перехода. Просто помните, что наличие обратного цикла for в существующем коде не обязательно означает, что порядок не имеет значения!
В общем, для кода более высокого уровня, где ясность и безопасность являются более важными, я ранее рекомендовал использовать _ 8_ в качестве шаблона по умолчанию для цикла (хотя в наши дни я предпочитаю использовать for..of
). Причины предпочесть forEach
обратному циклу:
for
и while
циклах).Затем, когда вы видите обратный цикл for в своем коде, это намек на то, что он отменен по уважительной причине (возможно, одна из причин, описанных выше). И видение традиционного прямого цикла может указывать на то, что может происходить переключение.
(Если обсуждение намерения для вас не имеет смысла, тогда вам и вашему коду может быть полезно посмотреть лекцию Крокфорда на Стиль программирования и ваш мозг.)
Ведутся споры о том, что предпочтительнее: 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.
Некоторые языки в стиле 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
для создания циклов, которые можно замкнуть накоротко.
Если вы хотите перебрать массив, используйте стандартный трехэлементный цикл for
.
for (var i = 0; i < myArray.length; i++) {
var arrayItem = myArray[i];
}
Вы можете добиться некоторой оптимизации производительности, кэшируя myArray.length
или повторяя его в обратном порядке.
Если вы не против очистить массив:
var x;
while(x = y.pop()){
alert(x); //do something
}
x
будет содержать последнее значение y
и будет удалено из массива. Вы также можете использовать shift()
, который предоставит и удалит первый элемент из y
.
Я знаю, что это старый пост, и уже есть так много отличных ответов. Для большей полноты я решил добавить еще один, используя 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
в этого плохого парня.
Реализация 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
}
);
Начиная с ECMAScript 6:
list = [0, 1, 2, 3]
for (let obj of list) {
console.log(obj)
}
Где of
избегает странностей, связанных с in
, и заставляет его работать как цикл for
любого другого языка, а let
связывает i
внутри цикла, а не внутри функции.
Скобки ({}
) можно опустить, когда есть только одна команда (например, в примере выше).
Возможно, петля 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
//!!!
Ниже приведены три реализации 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
Теперь простое решение - использовать библиотеку underscore.js. Он предоставляет множество полезных инструментов, таких как each
, и автоматически делегирует задание родному forEach
, если доступно.
var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});
Array.prototype.forEach()
.for each (variable in object)
устарел как часть ECMA-357 (EAX ) стандарт.for (variable of object)
как части предложения Harmony (ECMAScript 6).В собственном 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];
...
}
Это итератор для НЕ разреженного списка, где индекс начинается с 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 меняются на зеленые.
Предупреждение при использовании строки как функции: функция создается вне контекста и должна использоваться только в том случае, если вы уверены в области видимости переменной. В противном случае лучше передавать функции, где область видимости более интуитивна.
Есть несколько способов перебрать массив в 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: отображение, фильтрация и сокращение.
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]
Встроенной возможности взлома forEach
нет. Чтобы прервать выполнение, используйте Array#some
, как показано ниже:
[1,2,3].some(function(number) {
return number === 1;
});
Это работает, потому что some
возвращает истину, как только любой из обратных вызовов, выполняемых в порядке массива, возвращает истину, сокращая выполнение остальных. Исходный ответ см. прототип массива для некоторые
Я также хотел бы добавить это как композицию обратного цикла и ответ выше для тех, кому тоже понравится этот синтаксис.
var foo = [object,object,object];
for (var i = foo.length, item; item = foo[--i];) {
console.log(item);
}
Плюсы:
Преимущество этого: у вас уже есть ссылка в первой строке, и ее не нужно будет объявлять позже в другой строке. Это удобно при циклическом перемещении по массиву объектов.
Минусы:
Это будет прерываться всякий раз, когда ссылка ложная - ложная (неопределенная и т. Д.). Однако это можно использовать как преимущество. Однако читать будет немного сложнее. А также в зависимости от браузера он может быть «не» оптимизирован для работы быстрее оригинального.
Способ 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];
Использование циклов с деструктуризацией в 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);
});
Наиболее близким к вашей идее будет использование 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]
Сегодня (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 strong>, но для небольших массивов его можно использовать - для большей ясности кода.
Циклы с длиной массива, кэшированной в n
(Ab, Bb, Be), иногда быстрее, иногда нет. Вероятно, компиляторы автоматически обнаруживают эту ситуацию и вводят кеширование. Разница в скорости между кэшированной и не кэшированной версиями (Aa, Ba, Bd) составляет около ~ 1%, поэтому похоже, что вводить n
- это micro-optimisation.
Решения, подобные i--
, в которых цикл начинается с последнего элемента массива (Ac, Bc), обычно на ~ 30% медленнее, чем решения прямого действия - вероятно, причина в способе Кэш памяти ЦП работает - прямое чтение памяти более оптимально для кэширования ЦП ). Рекомендуется НЕ ИСПОЛЬЗОВАТЬ такие решения.
В тестах мы вычисляем сумму элементов массива. Я провожу тест для небольших массивов (10 элементов) и больших массивов (1M элементов) и делю их на три группы:
for
тестыwhile
тестов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. Вы можете выполнить тест на своем компьютере здесь
Лямбда-синтаксис обычно не работает в 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);
});
Вы можете вызвать 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
}
Если вы хотите использовать forEach()
, это будет выглядеть так -
theArray.forEach ( element => {
console.log(element);
});
Если вы хотите использовать for()
, это будет выглядеть так -
for(let idx = 0; idx < theArray.length; idx++){
let element = theArray[idx];
console.log(element);
}
Если вы хотите перебрать массив объектов с помощью функции стрелки:
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');
})
В соответствии с новой обновленной функцией 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
При итерации по массиву мы часто хотим достичь одной из следующих целей:
Мы хотим перебрать массив и создать новый массив:
Array.prototype.map
Мы хотим перебрать массив и не создавать новый массив:
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);
}
Если у вас массивный массив, вы должны использовать _1 _ strong>, чтобы повысить эффективность. Итераторы являются свойством определенных коллекций JavaScript (например, _2 _, Set
, _ 4_, _ 5_). Даже _6 _ strong > использует 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
здесь.
Вы можете использовать:
Для каждого
theArray.forEach(function (array, index) {
console.log(index);
console.log(array);
});
для
for(var i=0; i<theArray.length; i++) {
console.log(i)
}
карта
theArray.map(x => console.log(x));
карта
theArray.filter(x => console.log(x));
И есть много других для повторения.
Я бы сказал, что 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()
.
Предположим, у нас есть набор предметов:
let ddl = new Array();
if (subjects) {
subjects.forEach(function (s) {ddl.push({"id": s.id,"label": s.name});});
}
Если вы хотите сохранить функциональность своего кода, используйте map
:
theArray.map(instance => do_something);
Таким образом вы создадите новый массив для будущих операций и пропустите нежелательные побочные эффекты.
Вы можете использовать API forEach () (предоставляемый JavaScript), который принимает функцию как обратный вызов и запускается один раз для каждого элемента, присутствующего внутри массива.
См. Массивы в JavaScript: часть 2 а>.
Я пришел из 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);
});
Но в конце концов оба делают одно и то же.
/* 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>
Используя функцию grep в jQuery, например:
$.grep([0, 1, 2], function(n, i) {
return n > 0;
});