Published on  2020-07-13
Read 5 min

Использование циклов в JS

Базовые циклы for, while, do...while

Есть 3 базовых конструкции которые существуют с самых ранних стандартов ECMAScript и позволяют осуществлять итерацию по структурам данных:

  • for - наиболее часто распространенная конструкция
  • do...while - используется крайне редко, обычно предпочтение отдается в пользу for
  • while - используется редко, обычно предпочтение отдается в пользу for

C их помощью можно итерировать различные структуры данных, например массивы:

Цикл for

const arr = ['a', 'b', 'c'];

for (let index = 0; index < arr.length; index++) {
  console.log(arr[index]);
}

Цикл while

const arr = ['a', 'b', 'c'];

let index = 0;

while (index < arr.length) {
  console.log(arr[index]);
  index++;
}

Цикл do...while

const arr = ['a', 'b', 'c'];

let index = 0;

do {
	console.log(arr[index]);
} while (++index < arr.length);

Однако, данные циклы неудобны тем, что приходится вручную объявлять переменные index увеличивать их index++, следить за условием index < arr.length

Более того, если допустить ошибку есть вероятность влипнуть в бесконечный цикл при использовании while и do...while, а это отберет у вас драгоценное время разработки, которое вы потратите на то чтобы убить вкладку браузера, если конечно он не сделает это за вас, через какое-то время, но это не точно :)

Итерируемые структуры данных

Итерация с помощью метода forEach

Для итерации по массивам и некоторым другим коллекциям данных можно использовать встроенный метод forEach

const arr = ['a', 'b', 'c'];

arr.forEach((item, index) => {
  console.log(item);
});

Но у данного метода есть недостаток - он не поддерживает управляющие конструкции break и continue, и return false также не оборвет итерацию, поэтому следует использовать данный метод только в том случае когда нет необходимости оборвать процесс итерации

Чтобы исправить положение связанное с невозможностью использования break и continue на помощь приходит цикл for...of

Итерация с помощью метода for...of

Данный цикл появился с приходом стандарта ECMAScript 2015 (ES6) и позволяет итерировать различные структуры данных: массивы, Map/WeackMap, Set/WeackSet, NodeList

const arr = ['a', 'b', 'c'];

for (const item of arr) {
  console.log(item);
}

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

И все бы хорошо, но как заметно из сигнатуры данного цикла, в нем нет возможности использовать индекс итерируемых данныx.

Для решения данной проблемы можно использовать встроенный метод массива entries:

for (const [index, value] of ['a', 'b', 'c'].entries()) {
  console.log(index, value);
}

Итерация по свойствам объектов

Итерация с помощью метода for...in

Для итерации по объектам можно применять цикл for...in, но у него есть ряд недостатков:

  • перебор происходит по алгоритму: "сначала целочисленные свойства, затем свойства в порядке добавления" - подробнее тут

  • в отличии от for...of он перебирает все свойства объектов как собственные так и унаследованные

const parent = {
  color: 'red'
};

const child = {
  fruite: 'apple'
};

child.__proto__ = parent;

for (const prop in child) {
  console.log(child[prop]);  
} 

// 'apple'
// 'red'

Чтобы исправить, можно добавить проверку с использование метода hasOwnProperty который проверит собственное ли свойства:

const parent = {
  color: 'red'
};
  
const child = {
  fruite: 'apple'
};
  
child.__proto__ = parent;
  
for (const prop in child) {
  if (child.hasOwnProperty(prop)) {
    console.log(child[prop]);  
  }
}   

// 'apple'

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

  • Object.keys
  • Object.values
  • Object.entries

Они возвращают массив только собственных значений объекта:

for (const [key, value] of Object.entries(obj)) { /* logic */}  
for (const key of Object.keys(obj)) { /* logic */ }
for (const value of Object.values(obj)) { /* logic */ }

Вывод:

На практике целесообразно использовать следующие циклы:

  • forEach - если нет необходимости "оборвать" итерацию
  • for...of во всех других случаях, в том числе и для итерации по объектам