Перебор массива в JavaScript

Существует несколько способов перебора массивов в JavaScript: традиционные и новые, которые мы рассмотрим в этой статье.

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

Приведенный ниже пример преобразует массив, умножая каждое из значений его элементов на 2:

var ar = [1, 2, 3, 4, 5];
for (var i=0, len=ar.length; i<len; i++) {
    ar[i] *= 2; // умножение значения на 2
}
console.log( ar ); // [2, 4, 6, 8, 10]

Может понадобиться проверить каждый элемент массива, является ли он undefined (неопределенным) или null (пустым), корректный ли тип у него и т.д.

В следующем примере мы проверяем, являются ли значения элемента массива числовыми:

var ar = ['ноль', 1, 'два', 3, 'четыре', 5, null, 'шесть'];
var sum = 0; // содержит сумму числовых значений массива
for (var i=0, len=ar.length; i<len; i++) {
    // Проверка, чтобы убедиться, что текущее значение массива числовое
    if ( typeof ar[i] === 'number' ) {
        sum += ar[i]; // если да, добавляем это значение к сумме
    }
}
console.log( sum ); // 9

Приведенные выше примеры демонстрируют оптимизированную форму JavaScript цикла по массиву, использующего вторую переменную (len).

Она содержит длину массива, которая используется вместо традиционной формы цикла, где достижение конца массива проверяется на каждой итерации:

// традиционный цикл for проверяет длину массива ar в каждой итерации
for (var i = 0; i < ar.length; i++) {
    // здесь код
}

JavaScript также включает в себя цикл for-in, который используется для перебора элементов массива. Вместе с числовыми индексами он перебирает наследуемые свойства и менее эффективен, чем цикл for. Поэтому использовать его для перебора массивов не рекомендуется.

Методы перебирающие массив в ECMAScript 5

Подавляющее большинство браузеров поддерживают новые методы перебора массива, предоставляемые ECMAScript 5: forEach, map, и filter. Эти методы принимают функцию в качестве первого аргумента. Каждый элемент массива, в свою очередь, передается этой функции, которая принимает три аргумента: значение текущего элемента, его индекс и сам массив.

Функция, которую вы определяете, не должна использовать все три аргумента. В некоторых случаях вам понадобится использовать только значение элемента массива, что мы и покажем в наших примерах, демонстрирующих эти методы.

Метод forEach

Метод forEach перебирает элементы массива, как обычный JavaScript цикл for. Но вы не можете использовать оператор break для досрочного выхода, как в for. Метод forEach не возвращает значение.

В следующем примере мы объявляем массив и вызываем forEach. Передаем значение, индекс, и массив (v, i, a) в качестве аргумента функции, чтобы изменить массив, умножая каждое его значение на 2:

var ar = [1, 2, 3, 4, 5];
ar.forEach( function(v, i, ar) { ar[i] = v*2; } );
console.log( ar ); // [2, 4, 6, 8, 10]

В следующем примере мы создаем новый массив вместо того, чтобы преобразовать массив, вызванный forEach. Нам нужно только передать значение (v) для этого:

var ar = [1, 2, 3, 4, 5];
var ar2 = []; // новый массив
// передаем значение, умножаем на 2, и выводим массив
ar.forEach( function(v) { ar2.push(v*2); } );
// вид нового массива
console.log( ar2 ); // [2, 4, 6, 8, 10]

Значение элемента массива может быть использовано в JavaScript цикле по массиву forEach для любых целей. Но если вы хотите создать новый массив на основе значений существующего, то метод map подходит больше.

Метод map

Метод map создает новый массив. Каждый элемент из существующего массива передается аргументу функции map.

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

var ar = [1, 2, 3, 4, 5];
var ar2 = ar.map( function(v) { return v*2; } );
console.log( ar2 ); // [2, 4, 6, 8, 10]

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

var ar = ['янв', 'фев', 'мар', 'апр', 'май'];
var ar2 = ar.map( function(v) {
    return v.charAt(0).toUpperCase() + v.slice(1);
} );
console.log( ar2 ); // [ "Янв", "Фев", "Мар", "Апр", "Май" ]

Часто нужно проверять тип значения элемента массива, прежде чем воздействовать на него. Например, если массив содержит значения, которые не являются строками в JavaScript, будет выводиться сообщение об ошибке «TypeError».

Поэтому мы включаем проверку типа:

var ar = [1, 'янв', 'фев', 'мар', 'апр', 'май', false];
var ar2 = ar.map( function(v) {
    if ( typeof v === 'string' ) {
        return v.charAt(0).toUpperCase() + v.slice(1);
    }
} );
console.log( ar2 ); // [undefined, "Янв", "Фев", "Мар", "Апр", "Май", undefined]

Обратите внимание, что для значений, не являющихся строками, было возвращено undefined. Это происходит потому, что массив, возвращенный методом map, соответствует длине JavaScript созданного массива в цикле, для которого он вызывался. Это происходит даже в разреженных массивах.

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

var ar = [1, 'янв', 'фев', 'мар', 'апр', 'май', false];
var ar2 = ar.map( function(v) {
    if ( typeof v === 'string' ) {
        return v.charAt(0).toUpperCase() + v.slice(1);
    } else {
        return v;
    }
} );
console.log( ar2 ); // [ 1, "Jan", "Feb", "Mar", "Apr", "May", false ]

Что делать, если мы хотим, чтобы наш массив состоял только из элементов определенного типа? Для этого можем использовать метод filter.

Метод filter

Метод filter возвращает JavaScript созданный массив в цикле. Он выбирает из исходного массива и возвращает новый, состоящий из элементов, соответствующих заданным критериям. Мы можем использовать метод filter следующим образом, чтобы возвратить массив, содержащий только строковые значения:

var ar = [1, 'янв', 'фев', 'мар', 'апр', 'май', false];
var ar2 = ar.filter( function(v) {
    if ( typeof v === 'string' ) {
        return true;
    }
} );
console.log( ar2 ); // [ "янв", "фев", "мар", "апр", "май" ]

Метод filter проверяет каждый элемент массива, и его аргумент функции должен возвратить true или false, чтобы указать, следует ли включать текущий элемент из JavaScript цикла по массиву в возвращаемый массив или нет.

В этом примере используется оператор остатка от деления (%), с помощью которого формируется новый массив, содержащий только четные значения из исходного массива:

var ar = [1, 2, 3, 4, 5, 6];
var ar2 = ar.filter( function(v) { return v % 2 === 0; } );
console.log( ar2 ); // [2, 4, 6]

Метод filter пропускает отсутствующие элементы в разреженных массивах. Таким образом, он может быть использован для создания обычного массива из разреженного:

var ar = [1, , , 4]; // разреженный массив
// использование filter, чтобы вернуть плотную версию разреженного массива
var ar2 = ar.filter( function() { return true; } );
console.log( ar2 ); // [1, 4]

Перевод статьи “Javascript Array Iteration” был подготовлен дружной командой проекта Сайтостроение от А до Я.