Генерация строк в циклах — задача, часто возникающая на практике. Разберем на примере функции, которая генерирует список дел. Функция принимает на вход массив дел и возвращает строку, представляющую итоговый список. Немного усложним задачу, добавив к списку заголовок и напоминание:
const coll = ['Покормить кота', 'Купить молоко', 'Сделать уборку']
buildTodoList(coll)
// Список дел:
//   * Покормить кота
//   * Купить молоко
//   * Сделать уборку
// Напоминание: дела нужно выполнить сегодня. Не откладывать на завтра!
Как можно решить эту задачу в лоб:
- Создать переменную 
resultи записать в нееСписок дел на сегодня: - Пройтись циклом по элементам коллекции и дописать в результирующую строку очередной элемент начинающийся с перевода строки и 
* - Добавить в конце напоминание и вернуть 
resultиз функции 
const buildTodoList = (coll) => {
  let result = 'Список дел на сегодня:'
  for (const item of coll) {
    result = `${result}\n  * ${item}`
    // либо так: result += `\n  * ${item}`;
  }
  result = `${result}\nНапоминание: дела нужно выполнить сегодня. Не откладывать на завтра!`
  return result
}
Такой способ вполне рабочий, но для большинства языков программирования максимально неэффективный. Дело в том, что конкатенация и интерполяция порождают новую строчку вместо старой. Подобная ситуация повторяется на каждой итерации. Причем строка становится все больше и больше. Копирование строк приводит к серьезному расходу памяти и может влиять на производительность. Конечно, для большинства приложений данная проблема неактуальна из-за малого объема прогоняемых данных, но более эффективный подход не сложнее в реализации и обладает рядом плюсов. Поэтому стоит сразу приучить себя работать правильно.
Правильно, в случае с динамическими языками – формировать массив, который затем с помощью метода join() можно превратить в строку:
const buildTodoList = (coll) => {
  const parts = []
  for (const item of coll) {
    parts.push(`* ${item}`)
  }
  // Метод join объединяет элементы массива в строку
  // В качестве разделителя между значениями
  // используется то, что передано параметром. В нашем случае — это перевод строки
  const innerValue = parts.join('\n')
  const result = `Список дел:\n${innerValue}\nНапоминание: дела нужно выполнить сегодня. Не откладывать на завтра!`
  return result
}
Размер кода практически не изменился, но способ формирования результата стал другим. Вместо строки, сначала собирается массив, который затем превращается в строку с помощью метода join(). У такого подхода есть и дополнительные плюсы:
- Такой код проще отлаживать. Данные, представленные массивом, легче вычленять визуально и программно
 - Массив — это структура, с ним можно производить дополнительные манипуляции. С готовой строкой уже ничего особо не сделать
 
Регулируя разделитель, строки можно объединять разными способами. Например, через запятую с пробелом:
const parts = ['JavaScript', 'PHP', 'Python']
const output = parts.join(', ')
console.log(output) // => JavaScript, PHP, Python
Если каждое слово надо вывести на новой строчке, то в качестве разделителя используем символ перевода строки '\n':
const parts = ['JavaScript', 'PHP', 'Python']
// теперь каждое слово будет начинаться с новой строки
const output = parts.join('\n')
console.log(output) // =>
// JavaScript
// PHP
// Python
Последний пример особенно важен. Новички часто допускают ошибку и добавляют перевод строки в момент формирования массива, а не в join(). Посмотрите на пример с нашей функцией buildHTMLList():
// Неправильно
const parts = []
for (const item of coll) {
  parts.push(`\n  * ${item}`)
}
const innerValue = parts.join('') // разделителя нет
// Правильно
const parts = []
for (const item of coll) {
  parts.push(`* ${item}`)
}
const innerValue = parts.join('\n') // перевод строки
Дополнительные материалы
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.