Перебор свойств в объектах. Все способы перебора массива в JavaScript Замечание по объектам среды исполнения
The forEach() method executes a provided function once for each array element.
The source for this interactive example is stored in a GitHub repository. If you"d like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
Syntax
arr .forEach(callback(currentValue [, index [, array]]) [, thisArg ]);Parameters
callback Function to execute on each element, taking three arguments: currentValue The current element being processed in the array. index Optional The index of the current element being processed in the array. array Optional The array forEach() was called upon. thisArg Optional Value to use as this when executing callback .Return value
Description
forEach() calls a provided callback function once for each element in an array in ascending order. It is not invoked for index properties that have been deleted or are uninitialized (i.e. on sparse arrays, ).
callback is invoked with three arguments:
- the value of the element
- the index of the element
- the Array object being traversed
If a thisArg parameter is provided to forEach() , it will be used as callback"s this value. The this value ultimately observable by callback is determined according to the usual rules for determining the this seen by a function .
The range of elements processed by forEach() is set before the first invocation of callback . Elements which are appended to the array after the call to forEach() begins will not be visited by callback . If existing elements of the array are changed or deleted, their value as passed to callback will be the value at the time forEach() visits them; elements that are deleted before being visited are not visited. If elements that are already visited are removed (e.g. using shift()) during the iteration, later elements will be skipped - see example below .
forEach() executes the callback function once for each array element; unlike map() or reduce() it always returns the value undefined and is not chainable. The typical use case is to execute side effects at the end of a chain.
forEach() does not mutate the array on which it is called (although callback , if invoked, may do so).
There is no way to stop or break a forEach() loop other than by throwing an exception. If you need such behavior, the forEach() method is the wrong tool.
Early termination may be accomplished with:
Array methods: every() , some() , find() , and findIndex() test the array elements with a predicate returning a truthy value to determine if further iteration is required.
Examples
No operation for uninitialized values (sparse arrays)
const arraySparse = ; let numCallbackRuns = 0; arraySparse.forEach(function(element){ console.log(element); numCallbackRuns++; }); console.log("numCallbackRuns: ", numCallbackRuns); // 1 // 3 // 7 // numCallbackRuns: 3 // comment: as you can see the missing value between 3 and 7 didn"t invoke callback function.Converting a for loop to forEach
const items = ["item1", "item2", "item3"]; const copy = ; // before for (let i=0; iNote: In order to display the content of an array in the console, you can use console.table() which will print a formatted version of the array. The following example illustrates another way of doing so, using forEach() .
The following code logs a line for each element in an array:
Function logArrayElements(element, index, array) { console.log("a[" + index + "] = " + element); } // Notice that index 2 is skipped since there is no item at // that position in the array. .forEach(logArrayElements); // logs: // a = 2 // a = 5 // a = 9
Using thisArg
The following (contrived) example updates an object"s properties from each entry in the array:
Function Counter() { this.sum = 0; this.count = 0; } Counter.prototype.add = function(array) { array.forEach(function(entry) { this.sum += entry; ++this.count; }, this); // ^---- Note }; const obj = new Counter(); obj.add(); obj.count; // 3 obj.sum; // 16
Since the thisArg parameter (this) is provided to forEach() , it is passed to callback each time it"s invoked, for use as its this value.
An object copy function
The following code creates a copy of a given object. There are different ways to create a copy of an object; the following is just one way and is presented to explain how Array.prototype.forEach() works by using ECMAScript 5 Object.* meta property functions.
Function copy(obj) { const copy = Object.create(Object.getPrototypeOf(obj)); const propNames = Object.getOwnPropertyNames(obj); propNames.forEach(function(name) { const desc = Object.getOwnPropertyDescriptor(obj, name); Object.defineProperty(copy, name, desc); }); return copy; } const obj1 = { a: 1, b: 2 }; const obj2 = copy(obj1); // obj2 looks like obj1 now
If the array is modified during iteration, other elements might be skipped.
The following example logs "one", "two", "four". When the entry containing the value "two" is reached, the first entry of the whole array is shifted off, which results in all remaining entries moving up one position. Because element "four" is now at an earlier position in the array, "three" will be skipped. forEach() does not make a copy of the array before iterating.
Var words = ["one", "two", "three", "four"]; words.forEach(function(word) { console.log(word); if (word === "two") { words.shift(); } }); // one // two // four
Flatten an array
The following example is only here for learning purpose. If you want to flatten an array using built-in methods you can use Array.prototype.flat() (expected to be part of ES2019 and already implemented in some browsers).
/** * Flattens passed array in one dimensional array * * @params {array} arr * @returns {array} */ function flatten(arr) { const result = arr.forEach((i) => { if (Array.isArray(i)) { result.push(...flatten(i)) } else { result.push(i) } }) return result } // Usage const problem = , 8, 9]] flatten(problem) //
Specifications
Specification | Status | Comment |
---|---|---|
ECMAScript Latest Draft (ECMA-262) |
Draft | |
ECMAScript 2015 (6th Edition, ECMA-262) The definition of "Array.prototype.forEach" in that specification. |
Standard | |
ECMAScript 5.1 (ECMA-262) The definition of "Array.prototype.forEach" in that specification. |
Standard | Initial definition. Implemented in JavaScript 1.6. |
Browser compatibility
The compatibility table in this page is generated from structured data. If you"d like to contribute to the data, please check out https://github.com/mdn/browser-compat-data and send us a pull request.
Update compatibility data on GitHub
Desktop | Mobile | Server | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Chrome | Edge | Firefox | Internet Explorer | Opera | Safari | Android webview | Chrome for Android | Firefox for Android | Opera for Android | Safari on iOS | Samsung Internet | Node.js | |
forEach | Chrome Full support Yes | Edge Full support 12 | Firefox Full support 1.5 | IE Full support 9 | Opera Full support Yes | Safari Full support Yes | WebView Android Full support Yes | Chrome Android Full support Yes | Firefox Android Full support 4 | Opera Android Full support Yes | Safari iOS Full support Yes | Samsung Internet Android Full support Yes | nodejs Full support Yes |
12 Мар 2016
В современном JavaScript существуют, так называемые «перебирающие методы», которые применяются для перебора массивов. В этом уроке мы рассмотрим следующие методы:
forEach
Метод.forEach() используется для перебора массива. Он вызывает так называемую функцию callback, с помощью которой предаётся три параметра item, i, arr, где:
- item — элемент массива;
- i — порядковый номер массива;
- arr — сам массив который должен перебираться.
Чтобы проще было понять, как использовать данный метод рассмотрим пример:
Var user=["admin","pass",31]; user.forEach(function(item,i,user){ alert("Значение элемента под № " + i + " : " + item); });
Этот метод может использоваться вместо обычного цикла for.
filter
Метод.filter() используется для фильтрации, он также использует функцию callback, но создаёт новый массив если элементы в массиве подходят под значение true:
Var arr=; var newArr=arr.filter(function(number){ return number < 0; }); alert(newArr); // выведет -34,-4
В данном примере проверяются числа на отрицательные значения, а на выходе выводится новый массив с этими значениями. Вы можете придумать свои условия, совсем необязательно это должны быть числа.
every/some
Эти два метода схожи друг с другом и оба используются для проверки массива, только метод .every() возвращает значение true если все значения в массиве подходят под заданное условие, а метод .some() возвращает true если хотя бы одно значение подходит под условие:
Var arr=; alert(arr.every(function(number){ return number < 0; })); // выведет false
Надеюсь понятно, что если бы в примере выше использовался метод some то у нас бы вывелось значение true, вместо false.
map
Метод.map() трансформирует массив и получает из него новый. Всё делается посредством вызова callback-функции:
Var arr=; var newArr=arr.map(function(number){ return number*2; }); alert(newArr);
В данном примере мы получили новый массив с удвоенными значениями элементов.
reduce/reduceRight
Последние методы, которые мы с вами рассмотрим это reduce и reduceRight. Используются они для обработки каждого элемента массива с сохранением промежуточного результата. Метод перебирает каждый элемент слева на право, reduceRight наоборот. В отличие от других методов кроме функции callback здесь ещё указывается аргумент initialValue — «начальное значение». Плюс ко всему в функции callback указывается «промежуточный результат» — previousValue и currentItem — текущий элемент массива.
Давайте рассмотрим пример:
Function getSums(arr) { var result = ; if (!arr.length) return result; var totalSum = arr.reduceRight(function(sum, item) { result.push(sum); return sum + item; }); result.push(totalSum); return result; } alert(getSums()); // 1,3,6,10,15
Что произошло в данном примере? Мы создали функцию, которая позволяет получить новый массив с элементами, созданными из суммы предыдущих. Причем отчет элементов идет с конца. А вот более простой пример, в котором я создал функцию считающую сумму элементов в массиве:
Function getSum(arr) { var result = arr.reduce(function(sum, current) { return sum + current }); return result; }; alert(getSum()); Метки:
Здравствуйте! На прошлом уроке мы с вами рассмотрели что такое объекты и зачем они нужны, а сегодня разберем как работать со свойствами объекта и как собственно все эти свойства перебирать. Для этих целей используется цикл по свойствам for..in (почитать про циклы в JavaScript можно ).
Цикл for..in
Синтаксис:
For (key in obj) { /* ... действия с obj ... */ }
Цикл for..in последовательно будет перебирать свойства объекта obj, имя каждого свойства запишет в key.
Объявление переменной в цикле for (var key in obj)
В данном цикле можно объявить переменную key:
For (var key in menu1) { // ... }
Давайте рассмотрим пример перебора свойств объекта, используя цикл for…in:
Var menu = { width: 400, height: 300, title: "Menu My" }; for (var key in menu) { // этот код сработает для каждого свойства объекта // ..и выведет соответственно имя свойства и его значение alert("Ключ: " + key + " значение: " + menu); }
Хочу обратить ваше внимание, что в примере были использовали квадратные скобки menu. Это потому что, если имя свойства мы храним в переменной, то и обратиться к нему можно только через квадратные скобки, но не через точку.
Цикл for…of
Также появился новый цикл по обходу объектов и массивов. Его синтаксис очень напоминает цикл for…in, а различия в том, что он выводит не ключи или индексы массива, а его значения. Вот пример:
Var menu = { width: 400, height: 300, title: "Menu My" }; for (var key of menu) { // этот код сработает для каждого свойства объекта // ..и выведет соответственно значение свойства alert("значение: " + key +","); //400, 300, "Menu My" }
Количество свойств в объекте
А что, если вам надо узнать количество свойств в объекте? Как это можно сделать?
К сожалению готовых решений для этого вопроса нет.
Самый простой способ — это сделать цикл по свойствам и посчитать следующим образом:
Var menu = { width: 400, height: 300, title: "Menu My" }; var count = 0; for (var key in menu) { count++; } alert("Всего свойств: " + count);
Итоги
- Для перебора свойств объекта используется цикл по ключам: for (key in obj).
Задачи
Определите, пуст ли объект
Создайте функцию isEmptyObject(obj), которая возвращает true, если в объекте нет свойств и false – если хоть одно свойство есть.
Работать должно так:
Function isEmptyObject(obj) { /* ваш код */ } var obj = {}; alert(isEmptyObject(obj)); // true obj["8:30"] = "подъём"; alert(isEmptyObject(obj)); // false
Подсчитайте среднее арифметическое всех свойств объекта
Есть объект salary с зарплатами. Напишите код, который выведет среднее арифметическое по всем зарплатам.
Если объект пустой, то результат должен быть 0.
Например.
Это работает так:
// REQUIRES ECMASCRIPT 2015+ var s, myStringArray = ["Hello", "World"]; for (s of myStringArray) { // ... do something with s ... }
Или, что еще лучше, поскольку ECMAScript 2015 также предоставляет переменные в области блока с помощью let и const:
// REQUIRES ECMASCRIPT 2015+ const myStringArray = ["Hello", "World"]; for (const s of myStringArray) { // ... do something with s ... } // s is no longer defined here
Примечание о разреженных массивах: массив в JavaScript может фактически не хранить столько элементов, сколько указано в его length ; это сообщаемое число просто на единицу больше, чем самый высокий индекс, при котором хранится значение. Если массив содержит меньше элементов, чем указано в его длине, он называется разреженным. Например, вполне законно иметь массив с элементами только по индексам 3, 12 и 247; length такого массива указана как 248, хотя на самом деле он хранит только 3 значения. Если вы попытаетесь получить доступ к элементу по любому другому индексу, у массива появится undefined значение. Поэтому, когда вы хотите "перебрать" массив, у вас есть вопрос, на который нужно ответить: хотите ли вы перебрать весь диапазон, указанный его длиной, и обработать undefined для любых пропущенных элементов, или вы хотите обработать только элементы на самом деле подарок? Существует множество приложений для обоих подходов; это зависит только от того, для чего вы используете массив.
Если вы перебираете массив с помощью for .. of , тело цикла выполняется по length , а переменная управления циклом устанавливается undefined для любых элементов, которые на самом деле отсутствуют в массиве. В зависимости от деталей вашего кода "делать что-то", такое поведение может быть тем, что вы хотите, но если это не то, что вы хотите, вам следует использовать другой подход.
Конечно, некоторые разработчики не имеют иного выбора, кроме как использовать другой подход в любом случае, потому что по какой - то причине они ориентации версию JavaScript, который еще не поддерживается for ... of .
Пока ваша реализация JavaScript совместима с предыдущей версией спецификации ECMAScript (которая, например, исключает версии Internet Explorer до 9), вы можете использовать метод итератора Array#forEach вместо цикла. В этом случае вы передаете функцию, которая будет вызываться для каждого элемента в массиве:
Var myStringArray = [ "Hello", "World" ]; myStringArray.forEach(function(s) { // ... do something with s ... });
В отличие от for ... of , .forEach вызывает функцию только для элементов, которые на самом деле содержат значения. Если передать наш гипотетический массив с тремя элементами и длиной 248, он будет вызывать функцию только три раза, а не 248 раз. Он также различает отсутствующие элементы и элементы, которые на самом деле установлены как undefined ; для последнего он по-прежнему будет вызывать функцию, передавая в качестве аргумента undefined значение. Если это, как вы хотите обрабатывать разреженные массивы, .forEach может быть путь, даже если ваш переводчик поддерживает for ... of .
Последний вариант, который работает во всех версиях JavaScript, - это явный цикл подсчета . Вы просто считаете от 0 до единицы меньше длины и используете счетчик в качестве индекса. Основной цикл выглядит так:
Одним из преимуществ этого подхода является то, что вы можете выбирать, как обрабатывать разреженные массивы; Приведенный выше код будет запускать тело цикла по полной length с s установленным на undefined для любых отсутствующих элементов, как for .. of . Если вместо этого вы хотите обрабатывать только реально существующие элементы разреженного массива, такие как.forEach , вы можете добавить in индекс простую проверку: Var i, s, myStringArray = [ "Hello", "World" ], len = myStringArray.length;
for (i=0; i Присвоение значения длины локальной переменной (в отличие от включения полного выражения myStringArray.length в условие цикла) может существенно myStringArray.length на производительность, поскольку оно пропускает поиск свойств каждый раз до конца; при использовании Rhino на моей машине ускорение составляет 43%. Вы можете увидеть, что кэширование длины выполняется в предложении инициализации цикла, например так: Var i, len, myStringArray = [ "Hello", "World" ];
for (len = myStringArray.length, i=0; i Упомянутый другими синтаксис for ... in используется для циклического обхода свойств объекта; так как массив в JavaScript - это просто объект с числовыми именами свойств (и автоматически обновляемым свойством length), вы можете теоретически зациклить массив с ним. Но проблема заключается в том, что он не ограничивается числовыми значениями свойств (помните, что даже методы на самом деле являются просто свойствами, значение которых является замыканием), и при этом не гарантируется, что они будут повторяться в числовом порядке. Следовательно, синтаксис for ... in не должен использоваться для циклического перемещения по массивам. На данный момент есть три способа перебора элементов настоящего массива: Кроме того, в скором времени, с появлением нового стандарта ECMAScript 6 (ES 6), ожидается еще два способа: Если ваш проект рассчитан на поддержку возможностей стандарта ECMAScript 5 (ES5), вы можете использовать одно из его нововведений - метод forEach . Пример использования: Var a = ["a", "b", "c"];
a.forEach(function(entry) {
console.log(entry);
});
В общем случае использование forEach требует подключения библиотеки эмуляции es5-shim для браузеров, не имеющих нативной поддержки этого метода. К ним относятся IE 8 и более ранние версии, которые до сих пор кое-где еще используются. К достоинствам forEach относится то, что здесь не нужно объявлять локальные переменные для хранения индекса и значения текущего элемента массива, поскольку они автоматически передаются в функцию обратного вызова (колбек) в качестве аргументов. Если вас беспокоят возможные затраты на вызов колбека для каждого элемента, не волнуйтесь и прочитайте это . forEach предназначен для перебора всех элементов массива, но кроме него ES5 предлагает еще несколько полезных методов для перебора всех или некоторых элементов плюс выполнения при этом каких-либо действий с ними: Старый добрый for рулит
: Var a = ["a", "b", "c"];
var index;
for (index = 0; index < a.length; ++index) {
console.log(a);
}
Если длина массива неизменна в течение всего цикла, а сам цикл принадлежит критическому в плане производительности участку кода (что маловероятно), то можно использовать «более оптимальную» версию for с хранением длины массива: Var a = ["a", "b", "c"];
var index, len;
for (index = 0, len = a.length; index < len; ++index) {
console.log(a);
}
Теоретически этот код должен выполняться чуть быстрее, чем предыдущий. Если порядок перебора элементов не важен, то можно пойти еще дальше в плане оптимизации и избавиться от переменной для хранения длины массива, изменив порядок перебора на обратный: Var a = ["a", "b", "c"];
var index;
for (index = a.length - 1; index >= 0; --index) {
console.log(a);
}
Тем не менее, в современных движках JavaScript подобные игры с оптимизацией обычно ничего не значат. Если вам посоветуют использовать цикл for...in , помните, что перебор массивов - не то, для чего он предназначен . Вопреки распространенному заблуждению цикл for...in перебирает не индексы массива, а перечислимые свойства объекта. Тем не менее, в некоторых случаях, таких как перебор разреженных массивов , for...in может оказаться полезным, если только соблюдать при этом меры предосторожности, как показано в примере ниже:
// a - разреженный массив
var a = ;
a = "a";
a = "b";
a = "c";
for (var key in a) {
if (a.hasOwnProperty(key) &&
/^0$|^d*$/.test(key) &&
key <= 4294967294) {
console.log(a);
}
}
В данном примере на каждой итерации цикла выполняется две проверки: Конечно, такие проверки отнимут лишнее время при выполнении цикла. Но в случае разреженного массива этот способ более эффективен, чем цикл for , поскольку в этом случае перебираются только те элементы, которые явно определены в массиве. Так, в примере выше будет выполнено всего 3 итерации (для индексов 0, 10 и 10000) - против 10001 в цикле for . Чтобы не писать такой громоздкий код проверок каждый раз, когда требуется перебор массива, можно оформить его в виде отдельной функции: Function arrayHasOwnIndex(array, key) {
return array.hasOwnProperty(key) && /^0$|^d*$/.test(key) && key <= 4294967294;
}
Тогда тело цикла из примера значительно сократится: For (key in a) {
if (arrayHasOwnIndex(a, key)) {
console.log(a);
}
}
Рассмотренный выше код проверок является универсальным, подходящим для всех случаев. Но вместо него можно использовать более короткую версию, хотя формально и не совсем правильную, но, тем не менее, подходящую для большинства случаев: For (key in a) {
if (a.hasOwnProperty(key) && String(parseInt(key, 10)) === key) {
console.log(a);
}
}
ES6, пока все еще пребывающий в статусе черновика , должен ввести в JavaScript итераторы. Итератор
- это реализуемый объектом протокол, который определяет стандартный способ получения последовательности значений (конечной или бесконечной). Многие встроенные объекты, в т.ч. настоящие массивы, имеют итераторы по умолчанию. Простейший способ применения итератора в настоящих массивах - использовать новую конструкцию 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 или Function.apply . Например, если вы хотите применить 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). Поэтому важно тестировать работу методов Array.prototype в каждой среде исполнения (браузере), в которой планируется использование вашего приложения.I. Перебор настоящих массивов
1. Метод forEach и родственные методы
2. Цикл for
3. Правильное использование цикла for...in
4. Цикл for...of (неявное использование итератора)
Объект имеет итератор, если в нем определен метод next() - функция без аргументов, возвращающая объект с двумя свойствами:5. Явное использование итератора
II. Перебор массивоподобных объектов
1. Использование способов перебора настоящих массивов
2. Преобразование в настоящий массив
3. Замечание по объектам среды исполнения