Javascript цикл for по всем элементам массива

Содержание:

  • I. Перебор настоящих массивов
    1. Метод forEach и родственные методы
    2. Цикл for
    3. Правильное использование цикла for…in
    4. Цикл for…of (неявное использование итератора)
    5. Явное использование итератора

  • II. Перебор массивоподобных объектов
    1. Использование способов перебора настоящих массивов
    2. Преобразование в настоящий массив
    3. Замечание по объектам среды исполнения

I. Перебор настоящих массивов

На данный момент есть три способа перебора элементов настоящего массива:

  1. метод Array.prototype.forEach;
  2. классический цикл for;
  3. «правильно» построенный цикл for…in.

Кроме того, в скором времени, с появлением нового стандарта ECMAScript 6 (ES 6), ожидается еще два способа:

  1. цикл for…of (неявное использование итератора);
  2. явное использование итератора.

1. Метод forEach и родственные методы

Если ваш проект рассчитан на поддержку возможностей стандарта ECMAScript 5 (ES5), вы можете использовать одно из его нововведений — метод forEach.

Пример использования:

var a = [“a”, “b”, “c”];
a.forEach(function(entry) {
console.log(entry);
});

В общем случае использование forEach требует подключения библиотеки эмуляции es5-shim для браузеров, не имеющих нативной поддержки этого метода. К ним относятся IE 8 и более ранние версии, которые до сих пор кое-где еще используются.

К достоинствам forEach относится то, что здесь не нужно объявлять локальные переменные для хранения индекса и значения текущего элемента массива, поскольку они автоматически передаются в функцию обратного вызова (колбек) в качестве аргументов.

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

forEach предназначен для перебора всех элементов массива, но кроме него ES5 предлагает еще несколько полезных методов для перебора всех или некоторых элементов плюс выполнения при этом каких-либо действий с ними:

  • every — возвращает true, если для каждого элемента массива колбек возвращает значение приводимое к true.
  • some — возвращает true, если хотя бы для одного элемента массива колбек возвращает значение приводимое к true.
  • filter — создает новый массив, включающий те элементы исходного массива, для которых колбек возвращает true.
  • map — создает новый массив, состоящий из значений возращаемых колбеком.
  • reduce — сводит массив к единственному значению, применяя колбек по очереди к каждому элементу массива, начиная с первого (может быть полезен для вычисления суммы элементов массива и других итоговых функций).
  • 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 перебирает не индексы массива, а перечислимые свойства объекта.

Тем не менее, в некоторых случаях, таких как перебор разреженных массивов, 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]);
}
}

В данном примере на каждой итерации цикла выполняется две проверки:

  1. то, что массив имеет собственное свойство с именем key (не наследованное из его прототипа).
  2. то, что key — строка, содержащая десятичную запись целого числа, значение которого меньше 4294967294. Откуда берется последнее число? Из определения индекса массива в 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]);
}
}

4. Цикл for…of (неявное использование итератора)

ES6, пока все еще пребывающий в статусе черновика, должен ввести в JavaScript итераторы.

Итератор — это реализуемый объектом протокол, который определяет стандартный способ получения последовательности значений (конечной или бесконечной).
Итератор — это объект, в котором определен метод next() — функция без аргументов, возвращающая объект с двумя свойствами:

  1. done (boolean) — принимает значение true, если итератор достиг конца итерируемой последовательности. В противном случае имеет значение false.
  2. value — определяет значение, возвращаемое итератором. Может быть не определено (отсутствовать), если свойство done имеет значение true.

Многие встроенные объекты, в т.ч. настоящие массивы, имеют итераторы по умолчанию. Простейший способ применения итератора в настоящих массивах — использовать новую конструкцию for…of.

Пример использования for…of:

var val;
var a = [“a”, “b”, “c”];
for (val of a) {
console.log(val);
}

В приведенном примере цикл for…of неявно вызывает итератор объекта Array для получения каждого значения массива.

5. Явное использование итератора

Итераторы можно также использовать и явно, правда, в этом случае код становится значительно сложнее, по сравнению с циклом for…of. Выглядит это примерно так:

var a = [“a”, “b”, “c”];
var it = a.entries();
var entry;
while (!(entry = it.next()).done) {
console.log(entry.value[1]);
}

В данном примере метод Array.prototype.entries возвращает итератор, который используется для вывода значений массива. На каждой итерации entry.value содержит массив вида [ключ, значение].

II. Перебор массивоподобных объектов

Кроме настоящих массивов, в JavaScript встречаются также массивоподобные объекты. С настоящими массивами их роднит то, что они имеют свойство length и свойства с именами в виде чисел, соответствующие элементам массива. В качестве примеров можно назвать DOM коллекции NodeList и псевдомассив arguments, доступный внутри любой функции/метода.

1. Использование способов перебора настоящих массивов

Как минимум большинство, если не все, способы перебора настоящих массивов могут быть применены для перебора массивоподобных объектов.

Конструкции 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
});

Если в массивоподобном объекте имеется итератор, то его можно использовать явно или неявно для перебора объекта таким же способом, как и для настоящих массивов.

2. Преобразование в настоящий массив

Есть также еще один, очень простой, способ перебора массивоподобного объекта: преобразовать его в настоящий массив и использовать любой из рассмотренных выше способов перебора настоящих массивов. Для преобразования можно использовать универсальный метод Array.prototype.slice, который может быть применен к любому массивоподобному объекту. Делается это очень просто, как показано в примере ниже:

var trueArray = Array.prototype.slice.call(arrayLikeObject, 0);

Например, если вы хотите преобразовать коллекцию NodeList в настоящий массив, вам нужен примерно такой код:

var divs = Array.prototype.slice.call(document.querySelectorAll(“div”), 0);

Update: Как было отмечено в комментариях rock и torbasow, в ES6 вместо Array.prototype.slice можно использовать более наглядный метод Array.from.

3. Замечание по объектам среды исполнения

Если вы применяете методы Array.prototype к объектам среды исполнения (таких как DOM коллекции), то вы должны иметь в виду, что правильная работа этих методов не гарантирована во всех средах исполнения (в т.ч. в браузерах). Это зависит от поведения конкретного объекта в конкретной среде исполнения, если точнее, от того, как в этом объекте реализована абстрактная операция HasProperty. Проблема в том, что сам стандарт ES5 допускает возможность неправильного поведения объекта по отношению к этой операции (см. §8.6.2).

Поэтому важно тестировать работу методов Array.prototype в каждой среде исполнения (браузере), в которой планируется использование вашего приложения.

Источник

JS: Массивы

Работа с массивами почти всегда завязана на одновременную обработку всех его элементов. Это нужно при выводе списков на экран, при выполнении различных расчетов или проверке данных. Во всех этих случаях нужен механизм для перебора элементов массива. Самый простой способ сделать это – использовать цикл.

Обход

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

// Создаем массив
const userNames = [‘petya’, ‘vasya’, ‘evgeny’];

// Определяем цикл
// Начальное значение счетчика i = 0 – вычисляется один раз перед началом выполнения
// Условие остановки i < userNames.length – выполняется перед каждой итерацией
// Изменение счетчика i += 1 – выполняется после каждой итерации
for (let i = 0; i < userNames.length; i += 1) {
// Этот код выполняется для каждого элемента
console.log(userNames[i]);
}

// => ‘petya’
// => ‘vasya’
// => ‘evgeny’

https://repl.it/@hexlet/js-arrays-for-print

В данном коде создаём массив из трёх элементов — имён. Далее в цикле обходим массив и выводим на экран все имена так, что каждое имя оказывается на новой строке (console.log автоматически делает перевод строки).

Рассмотрим этот этап подробнее. При обходе массива циклом for счётчик, как правило, играет роль индекса в массиве. Он инициализируется нулём и увеличивается до userNames.length – 1, что соответствует индексу последнего элемента. Именно поэтому мы используем строгое сравнение < (меньше) в условном выражении i < userNames.length, а не <= (меньше либо равно).

А что, если нам нужно вывести значения в обратном порядке? Для этого есть два способа. Один — идти в прямом порядке, то есть от нулевого индекса до последнего, и каждый раз вычислять нужный индекс по такой формуле размер массива – 1 – текущее значение счётчика.

const userNames = [‘petya’, ‘vasya’, ‘evgeny’];

for (let i = 0; i < userNames.length; i += 1) {
const index = (userNames.length – 1) – i;
console.log(userNames[index]);
}

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

const userNames = [‘petya’, ‘vasya’, ‘evgeny’];

// Начальное значение i соответствует последнему индексу в массиве
for (let i = userNames.length – 1; i >= 0; i -= 1) {
console.log(userNames[i]);
}

При таком обходе проверка остановки должна быть именно на >=, иначе элемент с индексом 0 не попадет в цикл.

Изменение

Во время обхода массива его можно не только читать, но и модифицировать. Предположим, что перед нами стоит задача нормализации списка электронных адресов — например, приведение их к нижнему регистру. Тогда код будет выглядеть так:

const emails = [‘VASYA@gmAil.com’, ‘iGoR@yandex.RU’, ‘netiD@hot.CoM’];

console.log(emails);
// => [ ‘VASYA@gmAil.com’, ‘iGoR@yandex.RU’, ‘netiD@hot.CoM’ ]

for (let i = 0; i < emails.length; i += 1) {
const email = emails[i];

// toLowerCase() — стандартный метод js,
// преобразующий строку в нижний регистр
const normalizedEmail = email.toLowerCase();
// Заменяем значение
emails[i] = normalizedEmail;
}

console.log(emails);
// => [ ‘vasya@gmail.com’, ‘igor@yandex.ru’, ‘netid@hot.com’ ]

https://repl.it/@hexlet/js-arrays-for-update

Ключевая строчка: emails[i] = normalizedEmail;. В ней происходит перезапись элемента под индексом i.

Резюме

Цикл for можно комбинировать с массивами в любых вариантах. Массив не обязательно перебирать полностью и от начала до конца. Можно например смотреть только каждый второй элемент или двигаться до половины. Все это зависит от конкретной задачи.

Точно так же массивы сочетаются с while. Единственное что нужно массивам – индекс.

Дополнительные материалы

  1. Официальная документация

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят менторы из команды Хекслета или другие студенты.

Для полного доступа к курсу нужна профессиональная подписка

Профессиональная подписка откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, даст возможность обращаться за помощью к менторам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ

Зарегистрироваться

или
войти в аккаунт

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 115 курсов, 2000+ часов теории

  • 800 практических заданий в браузере

  • 250 000 студентов

Наши выпускники работают в компаниях:

Источник

Сегодня, в пятой части перевода курса по JavaScript, мы поговорим о массивах и циклах. Массивы используются в ходе решения множества задач. Часто с массивами работают, используя циклы.

→ Часть 1: первая программа, особенности языка, стандарты
→ Часть 2: стиль кода и структура программ
→ Часть 3: переменные, типы данных, выражения, объекты
→ Часть 4: функции
→ Часть 5: массивы и циклы
→ Часть 6: исключения, точка с запятой, шаблонные литералы
→ Часть 7: строгий режим, ключевое слово this, события, модули, математические вычисления
→ Часть 8: обзор возможностей стандарта ES6
→ Часть 9: обзор возможностей стандартов ES7, ES8 и ES9

Массивы

Массивы, объекты типа Array, развиваются вместе с остальными механизмами языка. Они представляют собой списки пронумерованных значений.

Первый элемент массива имеет индекс (ключ) 0, такой подход используется во многих языках программирования.

В этом разделе мы рассмотрим современные методы работы с массивами.

▍Инициализация массивов

Вот несколько способов инициализации массивов.

const a = []
const a = [1, 2, 3]
const a = Array.of(1, 2, 3)
const a = Array(6).fill(1) //инициализация каждого элемента массива, состоящего из 6 элементов, числом 1

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

const a = [1, 2, 3]
console.log(a) //[ 1, 2, 3 ]
const first = a[0]
console.log(first) //1
a[0] = 4
console.log(a) //[ 4, 2, 3 ]

Конструктор Array для объявления массивов использовать не рекомендуется.

const a = new Array() //не рекомендуется
const a = new Array(1, 2, 3) //не рекомендуется

Этот способ следует использовать лишь при объявлении типизированных массивов.

▍Получение длины массива

Для того чтобы узнать длину массива, нужно обратиться к его свойству length.

const l = a.length

▍Проверка массива с использованием метода every()

Метод массивов every() можно использовать для организации проверки всех их элементов с использованием некоего условия. Если все элементы массива соответствуют условию, функция возвратит true, в противном случае она возвратит false.

Этому методу передаётся функция, принимающая аргументы currentValue (текущий элемент массива), index (индекс текущего элемента массива) и array (сам массив). Он может принимать и необязательное значение, используемое в качестве this при выполнении переданной ему функции.
Например, проверим, превышают ли значения всех элементов массива число 10.

const a = [11, 12, 13]
const b = [5, 6, 25]
const test = el => el > 10
console.log(a.every(test)) //true
console.log(b.every(test)) //false

Здесь нас, в функции test(), интересует лишь первый передаваемый ей аргумент, поэтому мы объявляем её, указывая лишь параметр el, в который и попадёт соответствующее значение.

▍Проверка массива с использованием метода some()

Этот метод очень похож на метод every(), но он возвращает true, если хотя бы один из элементов массива удовлетворяет условию, заданному переданной ему функцией.

▍Создание массива на основе существующего массива с использованием метода map()

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

const a = [1, 2, 3]
const double = el => el * 2
const doubleA = a.map(double)
console.log(a) //[ 1, 2, 3 ]
console.log(doubleA) //[ 2, 4, 6 ]

▍Фильтрация массива с помощью метода filter()

Метод filter() похож на метод map(), но он позволяет создавать новые массивы, содержащие лишь те элементы исходных массивов, которые удовлетворяют условию, задаваемому передаваемой методу filter() функцией.

▍Метод reduce()

Метод reduce() позволяет применить заданную функцию к аккумулятору и к каждому значению массива, сведя массив к единственному значению (это значение может иметь как примитивный, так и объектный тип). Этот метод принимает функцию, выполняющую преобразования, и необязательное начальное значение аккумулятора. Рассмотрим пример.

const a = [1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => {
 return accumulator * currentValue
}, 1)
console.log(a) //24
//итерация 1: 1 * 1 = 1
//итерация 2: 1 * 2 = 2
//итерация 3: 2 * 3 = 6
//итерация 4: 6 * 4 = 24

Здесь мы ищем произведение всех элементов массива, описанного с помощью литерала, задавая в качестве начального значения аккумулятора 1.

▍Перебор массива с помощью метода forEach()

Метод массивов forEach() можно использовать для перебора значений массивов и для выполнения над ними неких действий, задаваемых передаваемой методу функцией. Например, выведем, по одному, элементы массива в консоль.

const a = [1, 2, 3]
a.forEach(el => console.log(el))
//1
//2
//3

Если при переборе массива нужно остановить или прервать цикл, то при использовании forEach() придётся выбрасывать исключение. Поэтому если в ходе решения некоей задачи может понадобиться прерывание цикла, лучше всего выбрать какой-нибудь другой способ перебора элементов массива.

▍Перебор массива с использованием оператора for…of

Оператор for…of появился в стандарте ES6. Он позволяет перебирать итерируемые объекты (в том числе — массивы). Вот как им пользоваться.

const a = [1, 2, 3]
for (let v of a) {
 console.log(v)
}
//1
//2
//3

На каждой итерации цикла в переменную v попадает очередной элемент массива a.

▍Перебор массива с использованием оператора for

Оператор for позволяет организовывать циклы, которые, в частности, можно использовать и для перебора (или инициализации) массивов, обращаясь к их элементам по индексам. Обычно индекс очередного элемента получают, пользуясь счётчиком цикла.

const a = [1, 2, 3]
for (let i = 0; i < a.length; i += 1) {
 console.log(a[i])
}
//1
//2
//3

Если, в ходе выполнения цикла, нужно пропустить его итерацию, можно воспользоваться командой continue. Для досрочного завершения цикла можно воспользоваться командой break. Если в цикле, например, расположенном в некоей функции, использовать команду return, выполнение цикла и функции завершится, а возвращённое с помощью return значение попадёт туда, откуда была вызвана функция.

▍Метод @@iterator

Этот метод появился в стандарте ES6. Он позволяет получать так называемый «итератор объекта» — объект, который в данном случае позволяет организовывать перебор элементов массива. Итератор массива можно получить, воспользовавшись символом (такие символы называют «известными символами») Symbol.iterator. После получения итератора можно обращаться к его методу next(), который, при каждом его вызове, возвращает структуру данных, содержащую очередной элемент массива.

const a = [1, 2, 3]
let it = a[Symbol.iterator]()
console.log(it.next().value) //1
console.log(it.next().value) //2
console.log(it.next().value) //3

Если вызвать метод next() после того, как будет достигнут последний элемент массива, он возвратит, в качестве значения элемента, undefined. Объект, возвращаемый методом next(), содержит свойства value и done. Свойство done принимает значение false до тех пор, пока не будет достигнут последний элемент массива. В нашем случае, если вызвать it.next() в четвёртый раз, он возвратит объект { value: undefined, done: true }, в то время как при трёх предыдущих вызовах этот объект имел вид { value: значение, done: false }.

Метод массивов entries() возвращает итератор, который позволяет перебирать пары ключ-значение массива.

const a = [1, 2, 3]
let it = a.entries()
console.log(it.next().value) //[0, 1]
console.log(it.next().value) //[1, 2]
console.log(it.next().value) //[2, 3]

Метод keys() позволяет перебирать ключи массива.

const a = [1, 2, 3]
let it = a.keys()
console.log(it.next().value) //0
console.log(it.next().value) //1
console.log(it.next().value) //2

▍Добавление элементов в конец массива

Для добавления элементов в конец массива используют метод push().

a.push(4)

▍Добавление элементов в начало массива

Для добавления элементов в начало массива используют метод unshift().

a.unshift(0)
a.unshift(-2, -1)

▍Удаление элементов массива

Удалить элемент из конца массива, одновременно возвратив этот элемент, можно с помощью метода pop().

a.pop()

Аналогичным образом, с помощью метода shift(), можно удалить элемент из начала массива.

a.shift()

То же самое, но уже с указанием позиции удаления элементов и их количества, делается с помощью метода splice().

a.splice(0, 2) // удаляет и возвращает 2 элемента из начала массива
a.splice(3, 2) // удаляет и возвращает 2 элемента, начиная с индекса 3

▍Удаление элементов массива и вставка вместо них других элементов

Для того чтобы, воспользовавшись одной операцией, удалить некие элементы массива и вставить вместо них другие элементы, используется уже знакомый вам метод splice().

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

const a = [1, 2, 3, 4, 5, 6]
a.splice(2, 3, ‘a’, ‘b’)
console.log(a) //[ 1, 2, ‘a’, ‘b’, 6 ]

▍Объединение нескольких массивов

Для объединения нескольких массивов можно воспользоваться методом concat(), возвращающим новый массив.

const a = [1, 2]
const b = [3, 4]
const c = a.concat(b)
console.log(c) //[ 1, 2, 3, 4 ]

▍Поиск элементов в массиве

В стандарте ES5 появился метод indexOf(), который возвращает индекс первого вхождения искомого элемента массива. Если элемент в массиве найти не удаётся — возвращается -1.

const a = [1, 2, 3, 4, 5, 6, 7, 5, 8]
console.log(a.indexOf(5)) //4
console.log(a.indexOf(23)) //-1

Метод lastIndexOf() возвращает индекс последнего вхождения элемента в массив, или, если элемент не найден, -1.

const a = [1, 2, 3, 4, 5, 6, 7, 5, 8]
console.log(a.lastIndexOf(5)) //7
console.log(a.lastIndexOf(23)) //-1

В ES6 появился метод массивов find(), который выполняет поиск по массиву с использованием передаваемой ему функции. Если функция возвращает true, метод возвращает значение первого найденного элемента. Если элемент найти не удаётся, функция возвратит undefined.

Выглядеть его использование может следующим образом.

a.find(x => x.id === my_id)

Здесь в массиве, содержащем объекты, осуществляется поиск элемента, свойство id которого равняется заданному.

Метод findIndex() похож на find(), но он возвращает индекс найденного элемента или undefined.

В ES7 появился метод includes(), который позволяет проверить наличие некоего элемента в массиве. Он возвращает true или false, найдя или не найдя интересующий программиста элемент.

a.includes(value)

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

a.includes(value, i)

▍Получение фрагмента массива

Для того чтобы получить копию некоего фрагмента массива в виде нового массива, можно воспользоваться методом slice(). Если этот метод вызывается без аргументов, то возвращённый массив окажется полной копией исходного. Он принимает два необязательных параметра. Первый задаёт начальный индекс фрагмента, второй — конечный. Если конечный индекс не задан, то массив копируется от заданного начального индекса до конца.

const a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(a.slice(4)) //[ 5, 6, 7, 8, 9 ]
console.log(a.slice(3,7)) //[ 4, 5, 6, 7 ]

▍Сортировка массива

Для организации сортировки элементов массива в алфавитном порядке (0-9A-Za-z) используется метод sort() без передачи ему аргументов.

const a = [1, 2, 3, 10, 11]
a.sort()
console.log(a) //[ 1, 10, 11, 2, 3 ]
const b = [1, ‘a’, ‘Z’, 3, 2, 11]
b.sort()
console.log(b) //[ 1, 11, 2, 3, ‘Z’, ‘a’ ]

Этому методу можно передать функцию, задающую порядок сортировки. Функция принимает, для сравнения двух элементов, параметры a и b. Она возвращает отрицательное число в том случае, если a меньше b по какому-либо критерию, 0 — если они равны, и положительное число — если a больше b. При написании подобной функции для сортировки числовых массивов она может возвратить результат вычитания a и b. Так, возврат результата вычисления выражения a – b означает сортировку массива по возрастанию, возврат результата вычисления выражения b – a даст сортировку массива по убыванию.

const a = [1, 10, 3, 2, 11]
console.log(a.sort((a, b) => a – b)) //[ 1, 2, 3, 10, 11 ]
console.log(a.sort((a, b) => b – a)) //[ 11, 10, 3, 2, 1 ]

Для того чтобы обратить порядок следования элементов массива можно воспользоваться методом reverse(). Он, так же, как и sort(), модифицирует массив для которого вызывается.

▍Получение строкового представления массива

Для получения строкового представления массива можно воспользоваться его методом toString().

a.toString()

Похожий результат даёт метод join(), вызванный без аргументов.

a.join()

Ему, в качестве аргумента, можно передать разделитель элементов.

const a = [1, 10, 3, 2, 11]
console.log(a.toString()) //1,10,3,2,11
console.log(a.join()) //1,10,3,2,11
console.log(a.join(‘, ‘)) //1, 10, 3, 2, 11

▍Создание копий массивов

Для создания копии массива путём копирования в новый массив значений исходного массива можно воспользоваться методом Array.from(). Он подходит и для создания массивов из массивоподобных объектов (из строк, например).

const a = ‘a string’
const b = Array.from(a)
console.log(b) //[ ‘a’, ‘ ‘, ‘s’, ‘t’, ‘r’, ‘i’, ‘n’, ‘g’ ]

Метод Array.of() тоже можно использовать для копирования массивов, а также для «сборки» массивов из различных элементов. Например, для копирования элементов одного массива в другой можно воспользоваться следующей конструкцией.

const a = [1, 10, 3, 2, 11]
const b = Array.of(…a)
console.log(b) // [ 1, 10, 3, 2, 11 ]

Для копирования элементов массива в некое место самого этого массива используется метод copyWithin(). Его первый аргумент задаёт начальный индекс целевой позиции, второй — начальный индекс позиции источника элементов, а третий параметр, необязательный, указывает конечный индекс позиции источника элементов. Если его не указать, в указанное место массива будет скопировано всё, начиная от начального индекса позиции источника до конца массива.

const a = [1, 2, 3, 4, 5]
a.copyWithin(0, 2)
console.log(a) //[ 3, 4, 5, 4, 5 ]

Циклы

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

▍Цикл for

Рассмотрим пример применения этого цикла.

const list = [‘a’, ‘b’, ‘c’]
for (let i = 0; i < list.length; i++) {
 console.log(list[i]) //значения, хранящиеся в элементах циклов
 console.log(i) //индексы
}

Как уже было сказано, прерывать выполнение такого цикла можно, используя команду break, а пропускать текущую итерацию и переходить сразу к следующей можно с помощью команды continue.

▍Цикл forEach

Этот цикл мы тоже обсуждали. Приведём пример перебора массива с его помощью.

const list = [‘a’, ‘b’, ‘c’]
list.forEach((item, index) => {
 console.log(item) //значение
 console.log(index) //индекс
})
//если индексы элементов нас не интересуют, можно обойтись и без них
list.forEach(item => console.log(item))

Напомним, что для прерывания такого цикла надо выбрасывать исключение, то есть, если при использовании цикла может понадобиться прервать его, лучше выбрать какой-нибудь другой цикл.

▍Цикл do…while

Это — так называемый «цикл с постусловием». Такой цикл будет выполнен как минимум один раз до проверки условия завершения цикла.

const list = [‘a’, ‘b’, ‘c’]
let i = 0
do {
 console.log(list[i]) //значение
 console.log(i) //индекс
 i = i + 1
} while (i < list.length)

Его можно прерывать с использованием команды break, можно переходить на его следующую итерацию командой continue.

▍Цикл while

Это — так называемый «цикл с предусловием». Если, на входе в цикл, условие продолжения цикла ложно, он не будет выполнен ни одного раза.

const list = [‘a’, ‘b’, ‘c’]
let i = 0
while (i < list.length) {
 console.log(list[i]) //значение
 console.log(i) //индекс
 i = i + 1
}

▍Цикл for…in

Этот цикл позволяет перебирать все перечислимые свойства объекта по их именам.

let object = {a: 1, b: 2, c: ‘three’}
for (let property in object) {
 console.log(property) //имя свойства
 console.log(object[property]) //значение свойства
}

▍Цикл for…of

Цикл for…of совмещает в себе удобство цикла forEach и возможность прерывать его работу штатными средствами.

//перебор значений
for (const value of [‘a’, ‘b’, ‘c’]) {
 console.log(value) //значение
}
//перебор значений и получение индексов с помощью `entries()`
for (const [index, value] of [‘a’, ‘b’, ‘c’].entries()) {
 console.log(index) //индекс
 console.log(value) //значение
}

Обратите внимание на то, что здесь, в заголовке цикла, используется ключевое слово const, а не, как можно было бы ожидать, let. Если внутри блока цикла переменные не нужно переназначать, то const нам вполне подходит.
Если сравнить циклы for…in и for…of, то окажется, что for…in перебирает имена свойств, а for…of — значения свойств.

Циклы и области видимости

С циклами и с областями видимости переменных связана одна особенность JavaScript, которая может доставить разработчику некоторые проблемы. Для того чтобы с этими проблемами разобраться, поговорим о циклах, об областях видимости, и о ключевых словах var и let.

Рассмотрим пример.

const operations = []
for (var i = 0; i < 5; i++) {
 operations.push(() => {
   console.log(i)
 })
}
for (const operation of operations) {
 operation()
}

В цикле производится 5 итераций, на каждой из которых в массив operations добавляется новая функция. Эта функция выводит в консоль значение счётчика цикла — i. После того, как функции добавлены в массив, мы этот массив перебираем и вызываем функции, являющиеся его элементами.

Выполняя подобный код можно ожидать результата, показанного ниже.


1
2
3
4

Но на самом деле он выводит следующее.

5
5
5
5
5

Почему это так? Всё дело в том, что в качестве счётчика цикла мы используем переменную, объявленную с использованием ключевого слова var.

Так как объявления подобных переменных поднимаются в верхнюю часть области видимости, вышеприведённый код аналогичен следующему.

var i;
const operations = []
for (i = 0; i < 5; i++) {
 operations.push(() => {
   console.log(i)
 })
}
for (const operation of operations) {
 operation()
}

В результате оказывается, что в цикле for…of, в котором мы перебираем массив, переменная i всё ещё видна, она равна 5, в результате, ссылаясь на i во всех функциях, мы выводим число 5.

Как изменить поведение программы таким образом, чтобы она делала бы то, что от неё ожидается?

Самое простое решение этой проблемы заключается в использовании ключевого слова let. Оно, как мы уже говорили, появилось в ES6, его использование позволяет избавиться от некоторых странностей, характерных для var.

В частности, в вышеприведённом примере достаточно изменить var на let и всё заработает так, как нужно.

const operations = []
for (let i = 0; i < 5; i++) {
 operations.push(() => {
   console.log(i)
 })
}
for (const operation of operations) {
 operation()
}

Теперь на каждой итерации цикла каждая функция, добавленная в массив operations, получает собственную копию i. Помните о том, что в данной ситуации нельзя использовать ключевое слово const, так как значение i в цикле меняется.

Ещё один способ решения этой проблемы, который часто применялся до появления стандарта ES6, когда ключевого слова let ещё не было, заключается в использовании IIFE.

При таком подходе значение i сохраняется в замыкании, а в массив попадает функция, возвращаемая IIFE и имеющая доступ к замыканию. Эту функцию можно выполнить тогда, когда в ней возникнет необходимость. Вот как это выглядит.

const operations = []
for (var i = 0; i < 5; i++) {
 operations.push(((j) => {
   return () => console.log(j)
 })(i))
}
for (const operation of operations) {
 operation()
}

Итоги

Сегодня мы поговорили о массивах и о циклах в JavaScript. Тема нашего следующего материала — обработка исключений, особенности использования точки с запятой и шаблонные литералы.

Уважаемые читатели! Какими методами для работы с массивами в JavaScript вы пользуетесь чаще всего?

Источник

Читайте также:  В какой период цикла лучше сдавать анализы на инфекции