Перебор массива. измерение скорости. javascript
Содержание:
reduce
Метод – также использует исходный массив для последовательной обработки каждого элемента.
С помощью данного метода можно сохранять промежуточный результат и возвращать новые объекты, массивы или, например, числа.
Пример .reduce #1
Чаще всего я использую , чтобы подсчитать общее количество или сумму чего-либо.
1const invoices =22,8,16,120;
2const totalInvoices = invoices.reduce((total, current)=>{
3return total + current;
4});
5console.log(totalInvoices);
6
Пример .reduce() #2
Еще один пример – учет количества экземпляров каждой единицы товара.
Давайте определим общее количество бананов, яблок и т.д. в нашем массиве:
1const fruits ='apples','bananas','oranges','apples','kiwi','apples';
2const fruitsCount = fruits.reduce((accum, curVal)=>{
3if(!accumcurVal){
4 accumcurVal=1;
5return accum;
6}
7 accumcurVal+=1;
8return accum;
9},{});
10
11console.log(fruitsCount);
12
Как добавить новые элементы в массив
Чтобы добавить новый элемент в конец массива, просто используйте метод push(), например:
var colors = ;
colors.push("Yellow");
document.write(colors); // Результат: Red,Green,Blue,Yellow
document.write(colors.length); // Результат: 4
Точно так же, чтобы добавить новый элемент в начале массива, используйте метод unshift(), например:
var colors = ;
colors.unshift("Yellow");
document.write(colors); // Результат: Yellow,Red,Green,Blue
document.write(colors.length); // Результат: 4
Так же, вы можете добавить несколько элементов в начало или конец массива. Для этого, разделите значения запятыми передавая их методам push() или unshift().
Работа с массивами JS — нечисловые ключи массива
Ключи — это числа, но они могут иметь любые имена:
arr = [] arr = 5 arr.prop = 10 // не делайте так
Но делать этого не рекомендуется. Числовые массивы подходят для числовых ключей, а JavaScript ассоциативный массив — для связанных пар ключ-значение. И смешивать их не стоит.
Массивы в JavaScript представляют собой хэш-таблицы с их преимуществами в плане производительности, но и с определенными недостатками.
Например, push/pop работают только с крайними элементами массива, поэтому они невероятно быстры.
push работает только с концом:
var arr =
arr.push("something")
alert(arr) // строка "array"
Методы shift/unshift медленные, потому что им нужно изменить нумерацию всего массива. Метод splice также может привести к изменению нумерации:
Таким образом, shift/unshift работают медленнее, чем push/pop. Чем больше массив, тем больше времени занимает в JavaScript сортировка массива.
Какой получится результат? Почему?
arr =
arr.push( function() { alert(this) } )
arr() // ?
Решение
Поскольку массивы являются объектами, arr<a href=»/..»>..</a> фактически является вызовом метода объекта, таким как obj<a href=»/method»>method</a>:
arr()
// то же самое что
arr()
// синтаксически это неправильно, но концептуально то же самое:
arr.2()
// переписанное в том же стиле, что и obj.method()
this = arr в таком случае передается функции, поэтому выводится содержимое arr.
arr =
arr.push( function() { alert(this) } )
arr() // "a","b",функция
Перебор массива
Перебор элементов можно выполнить различными способами.
Один из первых способов – это использовать цикл :
const students = ;
for (let i = 0, length = students.length; i < length; i++) {
console.log( students );
}
Второй способ – метод :
students.forEach(function(item) {
console.log( item );
});
Третий способ – цикл :
for (let item of students) {
console.log( item );
}
Если нужно с индексами:
const nums = ;
// 1 способ
for (let i = 0, length = nums.length; i < length; i++) {
console.log( `students = ${students}`);
}
// 2 способ
nums.forEach(function(item, index, array) {
// item – элемент, index – его индекс, array – массив
console.log( `nums = ${item}` );
});
reduce/reduceRight
Метод «arr.reduce(callback)» используется для последовательной обработки каждого элемента массива с сохранением промежуточного результата.
Это один из самых сложных методов для работы с массивами. Но его стоит освоить, потому что временами с его помощью можно в несколько строк решить задачу, которая иначе потребовала бы в разы больше места и времени.
Метод используется для вычисления на основе массива какого-либо единого значения, иначе говорят «для свёртки массива». Чуть далее мы разберём пример для вычисления суммы.
Он применяет функцию по очереди к каждому элементу массива слева направо, сохраняя при этом промежуточный результат.
Аргументы функции :
- – последний результат вызова функции, он же «промежуточный результат».
- – текущий элемент массива, элементы перебираются по очереди слева-направо.
- – номер текущего элемента.
- – обрабатываемый массив.
Кроме , методу можно передать «начальное значение» – аргумент . Если он есть, то на первом вызове значение будет равно , а если у нет второго аргумента, то оно равно первому элементу массива, а перебор начинается со второго.
Проще всего понять работу метода на примере.
Например, в качестве «свёртки» мы хотим получить сумму всех элементов массива.
Вот решение в одну строку:
Разберём, что в нём происходит.
При первом запуске – исходное значение, с которого начинаются вычисления, равно нулю (второй аргумент ).
Сначала анонимная функция вызывается с этим начальным значением и первым элементом массива, результат запоминается и передаётся в следующий вызов, уже со вторым аргументом массива, затем новое значение участвует в вычислениях с третьим аргументом и так далее.
Поток вычислений получается такой
В виде таблицы где каждая строка – вызов функции на очередном элементе массива:
| результат | |||
|---|---|---|---|
| первый вызов | |||
| второй вызов | |||
| третий вызов | |||
| четвёртый вызов | |||
| пятый вызов |
Как видно, результат предыдущего вызова передаётся в первый аргумент следующего.
Кстати, полный набор аргументов функции для включает в себя , то есть номер текущего вызова и весь массив , но здесь в них нет нужды.
Посмотрим, что будет, если не указать в вызове :
Результат – точно такой же! Это потому, что при отсутствии в качестве первого значения берётся первый элемент массива, а перебор стартует со второго.
Таблица вычислений будет такая же, за вычетом первой строки.
Метод arr.reduceRight работает аналогично, но идёт по массиву справа-налево.
Object.fromEntries: Object из Map
Мы только что видели, как создать из обычного объекта при помощи .
Есть метод , который делает противоположное: получив массив пар вида , он создаёт из них объект:
Мы можем использовать , чтобы получить обычный объект из .
К примеру, у нас данные в , но их нужно передать в сторонний код, который ожидает обычный объект.
Вот как это сделать:
Вызов возвращает массив пар ключ/значение, как раз в нужном формате для .
Мы могли бы написать строку ещё короче:
Это то же самое, так как ожидает перебираемый объект в качестве аргумента, не обязательно массив. А перебор как раз возвращает пары ключ/значение, так же, как и . Так что в итоге у нас будет обычный объект с теми же ключами/значениями, что и в .
Работа с массивами JS — подробнее об определении массива
new Array()
В JavaScript объявление массива можно осуществить с помощью другого синтаксиса:
var arr = Array("Apple", "Peach", "etc")
Он используется редко только потому, что квадратные скобки [] короче.
Также существует вероятность того, что new Array, вызываемый с одним числовым аргументом, создаст массив заданной длины с неопределенными элементами:
var arr = new Array(2,3) // Ок, мы имеем arr = new Array(2) // получили ли мы ? alert(arr) // нет! мы получили
В приведенном выше примере мы получили undefined, потому что new Array(number) создает пустой массив с параметром length равным number.
Это может быть весьма неожиданно. Но если вы знаете об этой особенности, вы можете использовать new Array(number), например, так:
var indent = new Array(5).join('a') // aaaa (4 элемента)
Это оптимизированный способ повторить строку.
Многомерный массив JS
Массивы в JavaScript могут хранить любой тип данных:
var arr = alert(arr) // Small array
Это можно использовать для создания многомерных массивов:
var matrix = , , ] alert(matrix) // центральный элемент
Работа с массивами JS — метод slice
Можно извлечь часть массива с помощью метода slice(begin):
var arr = ;
var arr2 = arr.slice(0,2) // принимает 2 элемента, начиная с 0
alert(arr2.join(', ')) // "Why, learn"
Обратите внимание, что этот метод не изменяет в JavaScript количество элементов в массиве, а копирует его часть. Можно опустить второй аргумент, чтобы получить все элементы, начиная с определенного индекса:
Можно опустить второй аргумент, чтобы получить все элементы, начиная с определенного индекса:
var arr = ;
var arr2 = arr.slice(1) // принимает все элементы, начиная с 1
alert(arr2.join(', ')) // "learn, JavaScript"
Метод поддерживает отрицательные индексы, так же, как String#slice.
7. When to use forEach()
is best used to iterate array items, without breaking, and having simultaneously some side-effect.
Side-effects examples are a mutation of an outer scope variable, I/O operations (HTTP requests), DOM manipulations, and alike.
For example, let’s select all input elements from the DOM and use to clear them:
The side effect in the callback function is clearing the value of the input field.
Keep in mind that you cannot normally break the iteration of (other than a tricky way to throw an error to stop the iteration, which is a cheap hack). The method will always iterate over all the items.
If your case requires an early break from the cycle, a better option is the classic for or for..of.
When the array iteration computes a result, without side-effects, a better alternative is to select an array method like:
- array.map()
- array.reduce()
- array.every()
- array.some()
For example, let’s determine whether all numbers of an array are even.
The first solution involves method:
The code determines correctly if all numbers are even. The problem is the impossibility to break after finding the first odd number .
For this situation, a better alternative is method:
doesn’t only make the code shorter. It is also optimal, because method breaks iterating after finding the first odd number.
Эффективность
Методы выполняются быстро, а методы – медленно.
Почему работать с концом массива быстрее, чем с его началом? Давайте посмотрим, что происходит во время выполнения:
Просто взять и удалить элемент с номером недостаточно. Нужно также заново пронумеровать остальные элементы.
Операция должна выполнить 3 действия:
- Удалить элемент с индексом .
- Сдвинуть все элементы влево, заново пронумеровать их, заменив на , на и т.д.
- Обновить свойство .
Чем больше элементов содержит массив, тем больше времени потребуется для того, чтобы их переместить, больше операций с памятью.
То же самое происходит с : чтобы добавить элемент в начало массива, нам нужно сначала сдвинуть существующие элементы вправо, увеличивая их индексы.
А что же с ? Им не нужно ничего перемещать. Чтобы удалить элемент в конце массива, метод очищает индекс и уменьшает значение .
Действия при операции :
Метод не требует перемещения, потому что остальные элементы остаются с теми же индексами. Именно поэтому он выполняется очень быстро.
Аналогично работает метод .
Методы перебора элементов
1. Перебор элементов массива с помощью метода forEach().
Пример:
2. Метод map() проходится по элементам массива и как результат возвращает новый массив.
Пример:
3. Метод filter() предназначен для фильтрации массива через функцию. Данный метод вернет новый массив в зависимости от истинности выполненного условия для каждого элемента.
Пример:
4. Метод every() используется для проверки значений массива на равенство. Если при вызове колкэк-функции каждый элемент массива будет соответствовать условиям, то данный метод вернет true.
Пример:
5. Метод some() похож на метод every(), но при этом он отличается тем, что возвращает true, если при вызове колбэк-функции хотя бы один из элементов будет равен указанному условию.
Пример:
6. Метод reduce() позволяет обойти каждый элемент массива с возможностью сохранения промежуточного результата. Reduce() часто используется в качестве замены нескольких методов, которые потребовалось бы применить для достижения аналогичного результата.
Пример:
7. Метод reduceRight() работает так же как и reduce(), но идет по элементам массива справа налево.
Что такое ассоциативный массив
Ассоциативный массив — это массив, у которого в качестве ключей используются строки.
Представить структуру данных ассоциативного массива можно как совокупность пар «ключ-значение». В ассоциативном массиве каждое значение связано с определённым ключом. Доступ к значению осуществляется по имени ключа.
Представить ассоциативный массив можно как ящичек, в котором находятся коробочки. Каждая коробочка в этом ящичке имеет имя (ключ) и содержимое (значение).
Для того чтобы найти нужную коробочку в ящичке нужно знать её имя (ключ). Зная имя (ключ) коробочки можно получить её содержимое (значение).
Map
Map – это коллекция ключ/значение, как и . Но основное отличие в том, что позволяет использовать ключи любого типа.
Методы и свойства:
- – создаёт коллекцию.
- – записывает по ключу значение .
- – возвращает значение по ключу или , если ключ отсутствует.
- – возвращает , если ключ присутствует в коллекции, иначе .
- – удаляет элемент по ключу .
- – очищает коллекцию от всех элементов.
- – возвращает текущее количество элементов.
Например:
Как мы видим, в отличие от объектов, ключи не были приведены к строкам. Можно использовать любые типы данных для ключей.
Map может использовать объекты в качестве ключей.
Например:
Использование объектов в качестве ключей – это одна из известных и часто применяемых возможностей объекта . При строковых ключах обычный объект может подойти, но для ключей-объектов – уже нет.
Попробуем заменить на в примере выше:
Так как – это объект, то все ключи он автоматически преобразует к строке, в итоге получился строковой ключ . Это не то, чего мы хотим.
Как объект сравнивает ключи
Чтобы сравнивать ключи, объект использует алгоритм . Это почти такое же сравнение, что и , с той лишь разницей, что считается равным . Так что также может использоваться в качестве ключа.
Этот алгоритм не может быть заменён или модифицирован.
Цепочка вызовов
Каждый вызов возвращает объект map, так что мы можем объединить вызовы в цепочку:
Пустые элементы
Массивы JavaScript допускают пустые элементы. Массив ниже синтаксически верный и имеет длину 3 элемента:
const arr = ; arr.length; // 3
Что еще более запутывает, так это то, что циклические конструкции трактуют иначе, чем . Ниже показано, как четыре циклических конструкции обрабатывают с пустым элементом. for/in и for/each пропускают пустой элемент, for и for/of — нет.
// Prints "a, undefined, c"
for (let i = 0; i < arr.length; ++i) {
console.log(arr);
}
// Prints "a, c"
arr.forEach(v => console.log(v));
// Prints "a, c"
for (let i in arr) {
console.log(arr);
}
// Prints "a, undefined, c"
for (const v of arr) {
console.log(v);
}
Если вам интересно, все 4 конструкции выведут «a, undefined, c» для .
Есть еще один способ добавить пустой элемент в массив:
// Equivalent to `` const arr = ; arr = 'e';
forEach() и for/in пропускают пустые элементы в массиве, for и for/of — нет. Поведение forEach() может вызвать проблемы, однако можно заметить, что дыры в массивах JavaScript, как правило, встречаются редко, поскольку они не поддерживаются в JSON:
$ node
> JSON.parse('{"arr":}')
{ arr: }
> JSON.parse('{"arr":}')
{ arr: }
> JSON.parse('{"arr":}')
SyntaxError: Unexpected token , in JSON at position 12
Таким образом, вам не нужно особо беспокоиться о дырах в пользовательских данных, если вы не предоставите своим пользователям доступ ко всей среде выполнения JavaScript.
Вывод: for/in и forEach() не реагируют на пустые элементы, также известные как «дыры», в массиве. Редко есть какая-либо причина рассматривать дыры как особый случай, а не рассматривать индекс как значение undefined. Если вы допускаете наличие дыр, ниже приведен пример файла .eslintrc.yml, который запрещает вызов forEach().
parserOptions:
ecmaVersion: 2018
rules:
no-restricted-syntax:
- error
- selector: CallExpression
message: Do not use `forEach()`, use `for/of` instead
Как создать строку из массива
Могут быть ситуации, когда вы просто хотите создать строку, объединив элементы массива. Для этого вы можете использовать метод join(). Этот метод принимает необязательный параметр, который является строкой-разделителем, которая добавляется между каждым элементом. Если вы опустите разделитель, то JavaScript будет использовать запятую (,) по умолчанию. В следующем примере показано, как это работает:
var colors = ;
document.write(colors.join()); // Результат: Red,Green,Blue
document.write(colors.join("")); // Результат: RedGreenBlue
document.write(colors.join("-")); // Результат: Red-Green-Blue
document.write(colors.join(", ")); // Результат: Red, Green, Blue
Вы также можете преобразовать массив в строку через запятую, используя toString(). Этот метод не принимает параметр разделителя, как это делает join(). Пример работы метода toString():
var colors = ; document.write(colors.toString()); // Результат: Red,Green,Blue
Добавление и удаление элементов
В JavaScript для добавления и удаления элементов имеются следующие методы:
- – для добавления одного или нескольких элементов в конец массива;
- – для добавления одного или нескольких элементов в начало массива;
- – для удаления последнего элемента;
- – для удаления первого элемента.
push — добавление элемента в конец массива
Метод push предназначен для добавления элемента в конец массива. Значение этого элемента указывается в качестве параметра данного метода. В качестве результата метод push возвращает количество элементов в массиве с учётом добавленного.
let namePlanets = ;
namePlanets.push("Юпитер"); // 5
console.log(namePlanets); //
pop — удаление последнего элемента из массива
Метод pop предназначен для удаления последнего элемента из массива. У данного метода нет параметров. В качестве результата он возвращает значение последнего (удалённого) элемента массива.
let namePlanets = ; namePlanets.pop(); // "Марс" console.log(namePlanets); //
shift — удаление первого элемента из массива
Метод shift предназначен для удаления первого элемента из массива, т.е. элемента, имеющего индекс 0. Все остальные элементы массива смещаются к началу, т.е. у каждого из них индекс уменьшается на 1. Данный метод в качестве результата возвращает значение удалённого элемента.
let namePlanets = ; namePlanets.shift(); // "Венера" console.log(namePlanets); //
unshift — добавление элемента в начало массива
Метод unshift предназначен для добавления элемента в начало массива (перед другими элементами). Значение этого элемента указывается в качестве параметра данного метода. В качестве результата данный метод возвращает количество элементов в массиве с учётом добавленного.
let namePlanets = ;
namePlanets.unshift("Венера"); // 5
console.log(namePlanets); //
Перебор объекта Set
Мы можем перебрать содержимое объекта set как с помощью метода , так и используя :
Заметим забавную вещь. Функция в у имеет 3 аргумента: значение , потом снова то же самое значение , и только потом целевой объект. Это действительно так, значение появляется в списке аргументов дважды.
Это сделано для совместимости с объектом , в котором колбэк имеет 3 аргумента. Выглядит немного странно, но в некоторых случаях может помочь легко заменить на и наоборот.
имеет те же встроенные методы, что и :
- – возвращает перебираемый объект для значений,
- – то же самое, что и , присутствует для обратной совместимости с ,
- – возвращает перебираемый объект для пар вида , присутствует для обратной совместимости с .
JavaScript new Array()
JavaScript has a built in array constructor .
But you can safely use instead.
These two different statements both create a new empty array named points:
const points = new Array();
const points = [];
These two different statements both create a new array containing 6 numbers:
const points = new Array(40, 100, 1, 5, 25, 10);
const points = ;
The keyword can produce some unexpected results:
// Create an array with three elements:
const points = new Array(40, 100, 1);
// Create an array with two elements:
const points = new Array(40, 100);
// Create an array with one element ???
const points = new Array(40);
A Common Error
const points = ;
is not the same as:
const points = New Array(40);
// Create an array with one element:
const points = ;
// Create an array with 40 undefined elements:
const points = new Array(40);
Ассоциативный массив — что это?
Под ассоциативным массивом подразумевают массив, в котором в качестве ключей применяются строки. То есть речь идёт о совокупности пар «ключ-значение». Таким образом, в ассоциативном массиве любое значение связано с конкретным ключом, а доступ к этому значению производится по имени ключа.
Мы можем представить ассоциативный массив в виде небольшого ящика, где находятся отделения. Каждое отделение имеет имя (это ключ) и содержимое (это значение). Естественно, чтобы найти нужное отделение в ящике, мы должны знать имя отделения (ключ). Зная это имя, мы сможем получить содержимое отделения (значение).
Set
Объект – это особый вид коллекции: «множество» значений (без ключей), где каждое значение может появляться только один раз.
Его основные методы это:
- – создаёт , и если в качестве аргумента был предоставлен итерируемый объект (обычно это массив), то копирует его значения в новый .
- – добавляет значение (если оно уже есть, то ничего не делает), возвращает тот же объект .
- – удаляет значение, возвращает , если было в множестве на момент вызова, иначе .
- – возвращает , если значение присутствует в множестве, иначе .
- – удаляет все имеющиеся значения.
- – возвращает количество элементов в множестве.
Основная «изюминка» – это то, что при повторных вызовах с одним и тем же значением ничего не происходит, за счёт этого как раз и получается, что каждое значение появляется один раз.
Например, мы ожидаем посетителей, и нам необходимо составить их список. Но повторные визиты не должны приводить к дубликатам. Каждый посетитель должен появиться в списке только один раз.
Множество – как раз то, что нужно для этого:
Альтернативой множеству может выступать массив для хранения гостей и дополнительный код для проверки уже имеющегося элемента с помощью arr.find. Но в этом случае будет хуже производительность, потому что проходит весь массив для проверки наличия элемента. Множество лучше оптимизировано для добавлений, оно автоматически проверяет на уникальность.
Массивы – это объекты?
Массивы в JavaScript не являются каким-то определённым типом данных. Это объекты, а точнее их определённый вид ().
Например, получим с помощью оператора тип данных:
const arr = ; console.log( typeof arr ); // "object"
А так как массивы являются объектами, то при его копировании, передаётся не он сам, а ссылка на него:
const names = ; const copyNames = names; copyNames = 'Петр'; console.log( names ); //
Для копирования массива можно воспользоваться следующими способами:
const names = ; // клонирование массива с помощью slice const cloneNames1 = names.slice(); // с помощью оператора spread const cloneNames2 = ; // с помощью Array.from const cloneNames3 = Array.from(names);
Конструктором массивов является :
const arr = ; console.log( arr.constructor === Array ); // true
При этом прототипом массивов является «Array.prototype»:
console.log( Object.prototype.toString.call(arr) ); // "" console.log( Object.prototype.toString.call(Array.prototype) ); // ""
Проверить содержит ли переменная массив можно следующим образом:
// 1 способ console.log( Array.isArray(arr) ); // true // 2 способ console.log( arr instanceof Array ); // true // 3 способ console.log( arr.constructor === Array ); // true
Объект Map (ассоциативный массив)
Тип данных предназначен для создания ассоциативных массив (пар «ключ-значение»). В качестве ключа можно использовать как примитивные, так и ссылочные типы данных. Это одно из основных отличий от ассоциативных массивов как объектов, у которых в качестве ключа можно использовать только строки.
Пример создание ассоциативного массива (экземпляра объекта Map):
// создание пустого ассоциативного массива var arr1 = new Map(); // создание ассоциативного массива и добавление в него 3 пары "ключ-значение" var arr = new Map(, , ]);
Узнать количество элементов в массиве можно осуществить с помощью свойства :
arr.size; // 3
Добавление элемента в массив (в экземпляр объекта Map) осуществляется с помощью метода :
// добавить в массив одну пару "ключ-значение"
arr.set('key4','value4');
// добавить в массив несколько пар "ключ-значение"
arr.set('key5','value5');
arr.set('key6','value6');
// или так
arr
.set('key5','value5')
.set('key6','value6');
Если в массиве есть уже такой ключ, то произойдёт установка нового значения, которое будет связано с ним:
arr.set('key1','new value');
Получить значение по ключу выполняется с помощью метода :
// получить значение, ассоциированное с ключом 'key4'
arr.get('key4'); // 'value4'
Проверить есть ли ключ в массиве можно посредством метода :
// есть ли в массиве arr ключ key2
arr.has('key2'); // true
Удалить из ассоциативного массива (экземпляра объекта Map) элемент по имени ключа можно с помощью метода :
arr.delete('key1'); // true
Данный метод возвращает , если данный ключ существовал в массиве, в противном случае он возвращает .
if (arr.delete('key1')) {
console.log('Запись с ключом "key1" удалена из массива!');
} else {
console.log('Запись с ключом "key1" не найдена в массиве!');
}
Очистить массив (удалить все элементы) можно выполнить с помощью метода .
arr.clear(); // очистим массив arr arr.size; // 0 (количество элементов)
Перебор ассоциативного массива (объекта Map) обычно осуществляется с помощью цикла . При этом итерацию можно организовать по ключам, значениям и записям ().
Перебор ключей можно осуществить посредством итерируемого объекта , возвращаемым методом :
for (let key of arr.keys()) {
console.log(key);
}
Для перебора значений можно воспользоваться итерируемым объектом , возвращаемым посредством методом :
for (let value of arr.values()) {
console.log(value);
}
Перебор записей ассоциативного массива с использованием метода :
for (let pair of arr.entries()) {
// pair - это массив
console.log(pair); // ключ
console.log(pair); // значение
console.log(`Ключ = ${pair}, значение = ${pair}`);
}
Данный метод используется по умолчанию в for…of, поэтому его можно опустить:
for (let pair of arr) {
console.log(`Ключ = ${pair}, значение = ${pair}`);
}
Кроме этого перебрать ассоциативный массив можно с помощью метода forEach.
arr.forEach(function(value,key) {
console.log('key = ' + key +', value = ' + value);
});
Преобразовать ассоциативный массив (объект Map) в JSON и обратно можно так:
let arr = new Map(, , , ]); // в JSON jsonStr = JSON.stringify(); // из JSON в Map mapArr = new Map(JSON.parse(jsonStr));
Async/Await и генераторы
Другой крайний случай с forEach() — это то, что он не совсем правильно работает с async/await или генераторами. Если ваш callback forEach() является синхронным, то это не имеет значения, но вы не сможете использовать await внутри callback forEach ():
async function run() {
const arr = ;
arr.forEach(el => {
// SyntaxError
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(el);
});
}
Вы также не сможете использовать yield:
function* run() {
const arr = ;
arr.forEach(el => {
// SyntaxError
yield new Promise(resolve => setTimeout(resolve, 1000));
console.log(el);
});
}
Но приведенные выше примеры отлично работают с for/of:
async function asyncFn() {
const arr = ;
for (const el of arr) {
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(el);
}
}
function* generatorFn() {
const arr = ;
for (const el of arr) {
yield new Promise(resolve => setTimeout(resolve, 1000));
console.log(el);
}
}
Даже если вы пометите свой callback forEach() как async, вам будет сложно заставить асинхронный метод forEach() работать последовательно. Например, приведенный ниже скрипт будет печатать 0-9 в обратном порядке.
async function print(n) {
// Wait 1 second before printing 0, 0.9 seconds before printing 1, etc.
await new Promise(resolve => setTimeout(() => resolve(), 1000 - n * 100));
// Will usually print 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 but order is not strictly
// guaranteed.
console.log(n);
}
async function test() {
.forEach(print);
}
test();
T
Вывод: если вы используете async/await или генераторы, помните, что forEach() является синтаксическим сахаром. Как сахар, его следует использовать экономно и не для всего.
Заключение
Как правило, for/of — это самый надежный способ перебора массива в JavaScript. Он более лаконичен, чем обычный цикл for, и не имеет такого количества граничных случаев, как for/in и forEach(). Основным недостатком for/of является то, что вам нужно проделать дополнительную работу для доступа к индексу массива (см. дополнение), и вы не можете строить цепочки кода, как вы можете это делать с помощью forEach(). Но если вы знаете все особенности forEach(), то во многих случаях его использование делает код более лаконичным.
Дополнение: Чтобы получить доступ к текущему индексу массива в цикле for/of, вы можете использовать функцию .
for (const of arr.entries()) {
console.log(i, v); // Prints "0 a", "1 b", "2 c"
}
Оригинал: For vs forEach() vs for/in vs for/of in JavaScript
Spread the love