- PVSM.RU - https://www.pvsm.ru -
На данный момент есть три способа перебора элементов настоящего массива:
Array.prototype.forEach;for;for...in.Кроме того, в скором времени, с появлением нового стандарта ECMAScript 6 (ES 6), ожидается еще два способа:
for...of (неявное использование итератора);Если ваш проект рассчитан на поддержку возможностей стандарта ECMAScript 5 (ES5), вы можете использовать одно из его нововведений — метод forEach [1].
Пример использования:
var a = ["a", "b", "c"];
a.forEach(function(entry) {
console.log(entry);
});
В общем случае использование forEach требует подключения библиотеки эмуляции es5-shim [2] для браузеров, не имеющих нативной поддержки этого метода. К ним относятся IE 8 и более ранние версии, которые до сих пор кое-где еще используются.
К достоинствам forEach относится то, что здесь не нужно объявлять локальные переменные для хранения индекса и значения текущего элемента массива, поскольку они автоматически передаются в функцию обратного вызова (колбек) в качестве аргументов.
Если вас беспокоят возможные затраты на вызов колбека для каждого элемента, не волнуйтесь и прочитайте это [3].
forEach предназначен для перебора всех элементов массива, но кроме него ES5 предлагает еще несколько полезных методов для перебора всех или некоторых элементов плюс выполнения при этом каких-либо действий с ними:
every — возвращает true, если для каждого элемента массива колбек возвращает значение приводимое к true.some — возвращает true, если хотя бы для одного элемента массива колбек возвращает значение приводимое к true.filter — создает новый массив, включающий те элементы исходного массива, для которых колбек возвращает true.map — создает новый массив, состоящий из значений возращаемых колбеком.reduce — сводит массив к единственному значению, применяя колбек по очереди к каждому элементу массива, начиная с первого (может быть полезен для вычисления суммы элементов массива и других итоговых функций).reduceRight — работает аналогично reduce, но перебирает элементы в обратном порядке.
Старый добрый 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 подобные игры с оптимизацией обычно ничего не значат.
Если вам посоветуют использовать цикл for...in, помните, что перебор массивов — не то, для чего он предназначен [4]. Вопреки распространенному заблуждению цикл for...in перебирает не индексы массива, а перечислимые свойства объекта.
Тем не менее, в некоторых случаях, таких как перебор разреженных массивов [5], 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]);
}
}
В данном примере на каждой итерации цикла выполняется две проверки:
key (не наследованное из его прототипа).key — строка, содержащая десятичную запись целого числа, значение которого меньше 4294967294. Откуда берется последнее число? Из определения [6] индекса массива в ES5, из которого следует, что наибольший индекс, который может иметь элемент в массиве: (2^32 - 2) = 4294967294.
Конечно, такие проверки отнимут лишнее время при выполнении цикла. Но в случае разреженного массива этот способ более эффективен, чем цикл for, поскольку в этом случае перебираются только те элементы, которые явно определены в массиве. Так, в примере выше будет выполнено всего 3 итерации (для индексов 0, 10 и 10000) — против 10001 в цикле for.
Чтобы не писать такой громоздкий код проверок каждый раз, когда требуется перебор массива, можно оформить его в виде отдельной функции:
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]);
}
}
ES6, пока все еще пребывающий в статусе черновика [7], должен ввести в JavaScript итераторы.
Итератор — это реализуемый объектом протокол, который определяет стандартный способ получения последовательности значений (конечной или бесконечной).
Объект имеет итератор, если в нем определен метод next() — функция без аргументов, возвращающая объект с двумя свойствами:
done (boolean) — принимает значение true, если итератор достиг конца итерируемой последовательности. В противном случае имеет значение false.value — определяет значение, возвращаемое итератором. Может быть не определено (отсутствовать), если свойство done имеет значение true.
Многие встроенные объекты, в т.ч. настоящие массивы, имеют итераторы по умолчанию. Простейший способ применения итератора в настоящих массивах — использовать новую конструкцию for...of.
Пример использования for...of:
var val;
var a = ["a", "b", "c"];
for (val of a) {
console.log(val);
}
В приведенном примере цикл for...of неявно вызывает итератор объекта Array для получения каждого значения массива.
Итераторы можно также использовать и явно, правда, в этом случае код становится значительно сложнее, по сравнению с циклом for...of. Выглядит это примерно так:
var a = ["a", "b", "c"];
var entry;
while (!(entry = a.next()).done) {
console.log(entry.value);
}
Кроме настоящих массивов, в JavaScript встречаются также массивоподобные объекты. С настоящими массивами их роднит то, что они имеют свойство length и свойства с именами в виде чисел, соответствующие элементам массива. В качестве примеров можно назвать DOM коллекции NodeList и псевдомассив arguments, доступный внутри любой функции/метода.
Как минимум большинство, если не все, способы перебора настоящих массивов могут быть применены для перебора массивоподобных объектов.
Конструкции for и for...in могут быть применены к массивоподобным объектам точно тем же путем, что и к настоящим массивам.
forEach и другие методы Array.prototype также применимы к массивоподобным объектам. Для этого нужно использовать вызов Function.call [8] или Function.apply [9].
Например, если вы хотите применить forEach к свойству childNodes объекта Node, то это делается так:
Array.prototype.forEach.call(node.childNodes, function(child) {
// делаем что-нибудь с объектом child
});
Для удобства повторного использования этого приема, можно объявить ссылку на метод Array.prototype.forEach в отдельной переменной и использовать ее как сокращение:
// (Предполагается, что весь код ниже находится в одной области видимости)
var forEach = Array.prototype.forEach;
// ...
forEach.call(node.childNodes, function(child) {
// делаем что-нибудь с объектом child
});
Если в массивоподобном объекте имеется итератор, то его можно использовать явно или неявно для перебора объекта таким же способом, как и для настоящих массивов.
Есть также еще один, очень простой, способ перебора массивоподобного объекта: преобразовать его в настоящий массив и использовать любой из рассмотренных выше способов перебора настоящих массивов. Для преобразования можно использовать универсальный метод Array.prototype.slice, который может быть применен к любому массивоподобному объекту. Делается это очень просто, как показано в примере ниже:
var trueArray = Array.prototype.slice.call(arrayLikeObject, 0);
Например, если вы хотите преобразовать коллекцию NodeList в настоящий массив, вам нужен примерно такой код:
var divs = Array.prototype.slice.call(document.querySelectorAll("div"), 0);
Если вы применяете методы Array.prototype к объектам среды исполнения (таких как DOM коллекции), то вы должны иметь в виду, что правильная работа этих методов не гарантирована во всех средах исполнения (в т.ч. в браузерах). Это зависит от поведения конкретного объекта в конкретной среде исполнения, если точнее, от того, как в этом объекте реализована абстрактная операция HasProperty. Проблема в том, что сам стандарт ES5 допускает возможность неправильного поведения объекта по отношению к этой операции (см. §8.6.2 [10]).
Поэтому важно тестировать работу методов Array.prototype в каждой среде исполнения (браузере), в которой планируется использование вашего приложения.
Автор: xmeoff
Источник [11]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/79580
Ссылки в тексте:
[1] forEach: http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
[2] es5-shim: https://github.com/es-shims/es5-shim
[3] это: http://blog.niftysnippets.org/2012/02/foreach-and-runtime-cost.html
[4] перебор массивов — не то, для чего он предназначен: http://blog.niftysnippets.org/2010/11/myths-and-realities-of-forin.html
[5] разреженных массивов: http://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D0%B6%D1%91%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2
[6] определения: http://www.ecma-international.org/ecma-262/5.1/#sec-15.4
[7] черновика: http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts
[8] Function.call: http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.4.4
[9] Function.apply: http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.4.3
[10] §8.6.2: http://www.ecma-international.org/ecma-262/5.1/#sec-8.6.2
[11] Источник: http://habrahabr.ru/post/247857/
Нажмите здесь для печати.