Перед тем, как рассказать, как следует перебирать элементы массивов, стоит сказать, как их не следует перебирать. Уж слишком распространён перебор элементов с помощью цикла for-in
.
var a = [0, 1, 2, 3, 4, 5, 6], props = '';
for (var i in a) {
props += i;
}
alert(props); // В большинстве случаев 0123456
Чем же так плох for-in
для перебора элементов массива? Тем, что он перебирает все свойства объекта, не имеющие атрибута DontEnum
. Из этого следуют как минимум 2 проблемы.
Во-первых, свойства не обязаны быть как-либо отсортированными, т.е. спецификация языка не обещает, что в данном случае элементы будут перебраны по порядку от 0
до length - 1
. Хотя на данный момент все JS-движки сортируют свойства массивов, а некоторые версии браузеров сортируют даже числовые свойства у обычных объектов. Поэтому данная проблема не очень актуальна.
Во-вторых, как уже было сказано, в таком случае перебираются все свойства массива, а не только числовые индексы. Обычно в таких случаях говорят, что не надо засорять Array.prototype
собственными методами, но даже без этого могут возникнуть проблемы. Возьмём, к примеру, массив, возвращаемый строковым методом match
.
var a = 'ab'.match(/((a)(b))/), props = '';
for (var i in a) {
props += i + ' ';
}
alert(props);
// IE: input index lastIndex 0 1 2 3
// Остальные браузеры: 0 1 2 3 index input
Т.е. стандартный метод строки возвращает массив, в котором помимо числовых индексов имеется несколько других свойств. Поэтому, если перебирать такой массив с помощью for-in
, мы получим не только числовые индексы.
Минимально возможный индекс в массиве — 0
, максимально возможный — length - 1
, поэтому для того, чтобы перебрать все элементы массива, нужно перебрать числа от 0
до length - 1
включительно. А для этого существует цикл for
.
var a = [0, 1, 2, 3, 4, 5], sum = 0;
for (var i = 0; i < a.length; i++) {
sum += a[i];
}
alert(sum); // 15
Если в массиве нет пропусков, т.е. в большинстве случаев, то такого цикла вполне достаточно. Однако если перебирается разреженный массив, то нужно проверять, существует ли элемент с текущим индексом.
var a = [1, 2, , , 3, , 5];
var props = '';
for (var i = 0; i < a.length; i++) {
props += a[i] + ' ';
}
alert(props); // 1 2 undefined undefined 3 undefined 5
// Перебрали и несуществующие элементы
props = '';
for (var i = 0; i < a.length; i++) {
if (i in a) {
props += a[i] + ' ';
}
}
alert(props); // 1 2 3 5
// Перебрали только существующие элементы
// Не забываем, что undefined обманчиво
a[2] = undefined;
props = '';
for (var i = 0; i < a.length; i++) {
if (i in a) {
props += a[i] + ' ';
}
}
alert(props); // 1 2 undefined 3 5
Наиболее правильной конструкцией для перебора элементов массива является
for (var i = 0, length = a.length; i < length; i++) {
if (i in a) {
// делаем что-то с элементом массива.
}
}
А чтобы не писать такие конструкции, лучше воспользоваться методом forEach
a.forEach(function(element, index) {
// делаем что-то с элементом element, у которого индекс index
});