Всё только о JavaScript

/ Статьи / Массивы в JavaScript

Как не надо перебирать элементы

Перед тем, как рассказать, как следует перебирать элементы массивов, стоит сказать, как их не следует перебирать. Уж слишком распространён перебор элементов с помощью цикла 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
});