Введение в массивы, цикл for of

В жизни мы часто сталкиваемся со списками: список студентов, марок машин, значений функции, элементов HTML-документа и т.п. Так вот, для хранения упорядоченных коллекций существует особая структура данных, которая называется массивом (по англ. array).

Обычно массивы в JavaScript задаются с помощью литерала – квадратных скобок:

let ar = [];  //пустой массив
let ar = [1, 30, 0, -2]; //одномерный массив чисел

Каждый элемент разделяется запятой. В результате мы здесь объявили массив из 4-х чисел. Или, так:

let ar = ["Яблоко", "Апельсин", "Слива"];

Здесь имеем массив из трех строковых элементов. Для обращения к отдельному элементу массива используется такой синтаксис:

<имя массива>[индекс элемента]

Например, выведем значение первого элемента массива ar в консоль:

console.log( ar[0] );

Обратите внимание, индекс первого элемента массива равен 0, не 1, как мы привыкли в повседневной жизни, а 0! Запомним это. Соответственно, второй элемент имеет индекс 1 и так далее:

console.log( ar[1] );
console.log( ar[2] );

Любой элемент массива можно изменить, используя тот же синтаксис:

ar[1] = 'Груша';

Теперь наш массив содержит элементы: "Яблоко", "Груша", "Слива". Вот так можно обращаться к элементам массива для считывания или изменения их значений.

В любой массив можно добавлять новые элементы, например, так:

ar[3] = 'Лимон';

Здесь мы указываем индекс элемента, в который хотим записать значение «Лимон» и, так как такого элемента нет, то он добавляется. В результате получаем массив из четырех строк:

"Яблоко", "Груша", "Слива", "Лимон"

У массива есть свойство length, хранящее значение равное максимальному индексу плюс один:

Array.length = maxIndex+1

Используя это свойство, можно через цикл вывести значения всех элементов массива:

for(let i=0;i < ar.length;++i)
    console.log( ar[i] );

Но можно это сделать и так:

console.log( ar );

Обратите внимание, length – это не число элементов массива в привычном нам виде. Например, если в наш массив добавить еще один элемент с большим индексом:

ar[999] = 'Виноград';

то length = 1000, но в массиве будет всего пять элементов:

"Яблоко", "Груша", "Слива", "Лимон" и "Виноград" (с индексом 999)

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

В JavaScript свойство length можно задавать вручную. Представим, имеется вот такой массив из чисел:

let dig = [1, 2, 3, 4, 5];

далее, мы уменьшаем его длину:

dig.length = 3;

теперь наш массив содержит только первые три элемента. Если попытаться восстановить длину снова до 5:

dig.length = 5;

то можем убедиться, что последние два элемента уже не существуют:

console.log( dig );

Вот так через свойство length можно обрезать длину массивов.

Язык JavaScript организован таким образом, что в массиве могут храниться элементы любого типа. Например:

let ar = [ 'Яблоко', { name: 'Джон' }, true, function() { alert('привет'); } ];

И выведем в консоль свойство name второго элемента:

console.log( ar[1].name );

Причем, массив не обязательно записывать на одной строке, можно и так:

let ar = [ 'Яблоко', { name: 'Джон' }, 
           true, 
           function() { alert('привет'); } ];

Массивы в JavaScript – это особого вида объекты, которые предназначены для работы с упорядоченными коллекциями данных и оптимизированы для работы с ними. Здесь ключевое слово – упорядоченные. Дело в том, что движок JavaScript старается хранить элементы массива в непрерывной области памяти, один за другим и это один из способов оптимизации. Но мы можем все это легко нарушить, если не будем рассматривать массивы именно как массивы. Например, можно создать пустой массив:

let digits = [];

а, затем, в очень дальний индекс запишем какое-либо значение:

digits[99999] = 1;

Технически так все будет работать, но оптимизации уже не будет. То есть, работать будет медленнее. Или, можно сделать даже так:

digits.zero = null;

Так как массив – это объект, то мы просто создаем свойство zero со значением null. Оптимизации здесь тоже не будет. Наконец, заполняя массив с конца:

digits[100] = 100;
digits[99] = 99;

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

Перебор элементов массива

Один из распространенных способов перебора элементов массива мы только что видели – это цикл for:

Например, для массива:

let fruits = ["Яблоко", "Апельсин", "Груша"];

перебрать его элементы можно так:

for(let i=0;i < fruits.length; ++i)
    console.log( fruits[i] );

мы увидим все значения элементов. Но есть и второй новый способ перебора с помощью цикла for of:

for(let value of fruits)
    console.log( value );

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

for(let value of fruits) {
     value = "none";
}
 
console.log(fruits);

В консоле мы увидим прежний массив. А вот через первый цикл так делать можно:

for(let i=0;i < fruits.length; ++i)
    fruits[i] = "none";

Преимуществом цикла for of является его оптимизация под массивы. В частности, он работает в 10-100 раз быстрее цикла for in, который мы рассматривали ранее, для перебора свойств объекта. Формально, мы могли бы использовать и его:

for(let key in fruits)
    console.log( fruits[key] );

Но это будет медленнее и к тому же там мы будем перебирать все публичные ключи массива, а не только целочисленные, которые являются индексами элементов массива. В общем, вывод такой. Нужно перебрать массив, используйте или обычный цикл for или цикл for of.

Видео по теме