for относится к низкоуровневым циклам. Он требует задания счетчика, правил его изменения и условия остановки. Было бы значительно удобнее обходить элементы коллекции напрямую, без счетчика. Многие языки программирования решают это введением специального вида цикла. В JavaScript тоже есть такой: for...of.
const userNames = ['petya', 'vasya', 'evgeny']
// name на каждой итерации свой собственный (локальный), поэтому используется const
for (const name of userNames) {
  console.log(name)
}
// => "petya"
// => "vasya"
// => "evgeny"
Как видно из примера, код, использующий for...of, получается значительно чище, чем с использованием цикла for. for...of знает о том, как перебирать элементы и знает о том, когда они закончатся. Поэтому счетчик не используется, а вместо доступа к элементу по индексу, например userNames[i], в цикле создается переменная const name. На каждой итерации она принимает значение элемента массива userNames, область видимости константы ограничена телом цикла.
Этот цикл отлично подходит для задач агрегации:
const calculateSum = (coll) => {
  let sum = 0
  for (const value of coll) {
    sum += value
  }
  return sum
}
for...of — это больше, чем просто цикл для массивов. Для полного понимания принципов его работы, нужно разбираться в темах, которые мы еще не проходили, среди них объекты, упаковка/распаковка и итераторы. Если по-простому, то разные данные в JavaScript могут притворяться коллекциями элементов. Самый простой пример — это строка: for...of перебирает строку посимвольно.
const greeting = 'Hello'
// В этот момент со строкой происходит магия, которая разбирается в курсе ООП
for (const symbol of greeting) {
  console.log(symbol)
}
// => "H"
// => "e"
// => "l"
// => "l"
// => "o"
Однако не следует путать строку с массивом. Несмотря на внешнюю схожесть доступа к элементам строки по индексу, строка массивом не является.
Применимость
В большинстве задач, использующих цикл, предпочтительнее for...of. Иногда его бывает недостаточно, и требуется ручное управление обходом. В таких случаях можно возвращаться к использованию for. Например, когда нужно идти не по каждому элементу массива, а через один:
for (let i = 0; i < items.length; i += 2) {
  // какой-то код
}
Иногда нужно обходить массив в обратном порядке. for...of здесь бессилен и снова нужен for:
for (let i = items.length - 1; i >= 0; i -= 1) {
  // какой-то код
}
Другие задачи вообще с массивами напрямую не связаны. К последним относятся ситуации, когда нужно перебирать числа в определенном диапазоне. В этом случае нет массива, по которому можно было бы пройтись с помощью for...of.
for (let i = 5; i < 10; i += 1) {
  // какой-то код
}
Ну и наконец, встречаются задачи, в которых нужно во время обхода менять исходный массив:
for (let i = 0; i < items.length; i += 1) {
  items[i] = /* что-то делаем */
}
Если заглядывать в будущее и в то, как пишется реальный код на JavaScript, то там появляются функции высшего порядка. То есть на практике циклы, можно сказать, не нужны за редким исключением. Однако, невозможно перепрыгнуть работу с циклами, так как это база. А функции высшего порядка требуют понимания таких тем, которые за один присест не изучаются.
Дополнительные материалы
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.