JavaScript: перебор массива. Основные способы перебора массива в JS
В этой статье мы рассмотрим три основных способа перебора элементов настоящего массива. Кроме того, посмотрим, как выполнять перебор массивоподобных объектов в JavaScript.
А. Перебор настоящих массивов
Для этого используются:
1. Известный метод
Что же, давайте рассмотрим эти методы подробнее.
1. Метод forEach
Пример использования:
var a = ["a", "b", "c"]; a.forEach(function(entry) { console.log(entry); });
Достоинства forEach заключаются в том, что вам не надо объявлять локальные переменные, чтобы хранить индекс и значения текущего элемента массива, так как они автоматически передаются в функцию обратного вызова (так называемый колбэк) в качестве аргументов.
С помощью forEach вы не только сможете выполнить перебор всех элементов массива, но и получите возможность выполнения некоторых действий с массивами: 1) some — возвращает true, когда хотя бы для одного элемента массива колбэк возвращает значение, приводимое к true; 2) every — возвращает true, когда для каждого элемента массива колбэк возвращает значение, приводимое к true; 3) filter — обеспечивает создание нового массива, включающего те элементы исходного, для коих колбэк возвращает true; 4) reduce — сводит массив к единственному значению, т. е. колбэк применяется по очереди к каждому элементу массива, начиная с 1-го (полезно при вычислении суммы элементов массива и прочих итоговых функций); 5) map — обеспечивает создание нового массива, состоящего из значений, которые возвращаются колбэком; 6) reduceRight — работает так же, как и reduce с той лишь разницей, что перебирает элементы в обратном порядке.
2. Цикл for
Что тут скажешь — старый добрый for…
var a = ["a", "b", "c"]; var index; for (index = 0; index < a.length; ++index) { console.log(a[index]); }
Кстати, когда длина массива неизменна в течение цикла, а цикл принадлежит критическому с точки зрения производительности участку кода (что маловероятно), подходит «более оптимальная» версия for с хранением длины массива:
var a = ["a", "b", "c"]; var index, len; for (index = 0, len = a.length; index < len; ++index) { console.log(a[index]); }
По идее, данный код должен выполняться немного быстрее предыдущего.
Если же порядок перебора элементов не особо важен, можно выполнить очередную оптимизацию, избавившись от переменной хранения длины массива и изменив прямой порядок перебора на обратный:
var a = ["a", "b", "c"]; var index; for (index = a.length - 1; index >= 0; --index) { console.log(a[index]); }
Однако справедливости ради стоит отметить, что в современных движках JavaScript вышеописанные игры с оптимизацией мало что значат.
3. Правильное использование цикла for...in
Вообще, цикл for...in не предназначен для перебора массивов. Он перебирает не индексы нашего массива, а перечисляемые свойства объекта.
Однако, если нам нужен перебор разреженных массивов, цикл for...in может быть весьма полезным, если, разумеется, соблюдать меры предосторожности:
// a — разреженный массив var a = []; a[0] = "a"; a[10] = "b"; a[10000] = "c"; for (var key in a) { if (a.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) { console.log(a[key]); } }
В вышеописанном примере на каждой циклической итерации осуществляются 2 проверки: 1) то, что массив имеет своё свойство с именем key (ненаследованное из его прототипа); 2) то, что key — это строка, содержащая десятичную запись целого числа, значение которого менее 4294967294.
Да, такие проверки могут отнять много времени, но если мы имеем дело с разреженным массивом, данный способ эффективнее обычного цикла for, т. к. в последнем случае перебираются лишь элементы, которые определены в массиве явно. Например в коде выше произойдёт всего 3 итерации (для индексов 0, 10 и 10000), в то время как при использовании классического for — 10001 итерация.
Кстати, код проверок можете оформить в виде отдельной функции:
function arrayHasOwnIndex(array, key) { return array.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294; }
В таком случае тело цикла существенно сократится:
for (key in a) { if (arrayHasOwnIndex(a, key)) { console.log(a[key]); } }
Вышеописанный код универсален, но вы можете использовать версию и короче. Формально она не совсем правильна, зато подходит практически для любых случаев:
for (key in a) { if (a.hasOwnProperty(key) && String(parseInt(key, 10)) === key) { console.log(a[key]); } }
Б. Перебор массивоподобных объектов
В JavaScript есть не только настоящие массивы, но и массивоподобные объекты. У них есть свойство length и свойства с именами в виде чисел, которые соответствуют элементам массива. Это DOM-коллекции NodeList либо псевдомассив arguments, доступный внутри любого метода/функции.
1. Применяем способы перебора настоящих массивов
Практически все способы перебора настоящих массивов можно применять для перебора массивоподобных объектов. Например, при использовании конструкций for и for...in всё делается тем же путём.
Что касается
Допустим, вы желаете применить
Array.prototype.forEach.call(node.childNodes, function(child) { // делаем что-либо с объектом child });
Чтобы было удобнее повторно использовать этот приём, объявите ссылку на метод
// (Считаем, что весь код ниже находится в одной области видимости) var forEach = Array.prototype.forEach; // ... forEach.call(node.childNodes, function(child) { // делаем что-либо с объектом child });
Когда в массивоподобном объекте есть итератор, его можно задействовать явно либо неявно для перебора объекта тем же способом, как и в случае с настоящими массивами.
2. Преобразование массивоподобного объекта в настоящий массив
Простой способ перебора — преобразовать массивоподобный объект в настоящий массив. Для этого подходит, универсальный метод
var trueArray = Array.prototype.slice.call(arrayLikeObject, 0);
Если же желаете преобразовать коллекцию NodeList в настоящий массив, то лучше действовать несколько иначе:
var divs = Array.prototype.slice.call(document.querySelectorAll("div"), 0);
Кроме того, вместо
На этом всё, удачного вам кодинга!