Генерация строк в циклах — задача, часто возникающая на практике. Типичный пример — функция, помогающая генерировать HTML-списки. Она принимает на вход коллекцию элементов и возвращает HTML-список из них:
const coll = ['milk', 'butter'];
buildHTMLList(coll);
// <ul><li>milk</li><li>butter</li></ul>
Как можно решить эту задачу в лоб:
- Создать переменную
result
и записать в нее<ul>
- Пройтись циклом по элементам коллекции и дописать в результирующую строку очередной элемент
<li>
- Добавить в конце
</ul>
и вернутьresult
из функции
const buildHTMLList = (coll) => {
let result = '<ul>';
for (const item of coll) {
result = `${result}<li>${item}</li>`;
// либо так: result += `<li>${item}</li>`;
}
result = `${result}</ul>`;
return result;
}
Такой способ вполне рабочий, но для большинства языков программирования максимально неэффективный. Дело в том, что конкатенация и интерполяция порождают новую строчку вместо старой. Подобная ситуация повторяется на каждой итерации. Причем строка становится все больше и больше. Копирование строк приводит к серьезному расходу памяти и может влиять на производительность. Конечно, для большинства приложений данная проблема неактуальна из-за малого объема прогоняемых данных, но более эффективный подход не сложнее в реализации и обладает рядом плюсов. Поэтому стоит сразу приучить себя работать правильно.
Правильно, в случае с динамическими языками – формировать массив, который затем с помощью метода join()
можно превратить в строку:
const buildHTMLList = (coll) => {
const parts = [];
for (const item of coll) {
parts.push(`<li>${item}</li>`);
}
// Метод join объединяет элементы массива в строку
// В качестве разделителя между значениями
// используется то, что передано параметром
const innerValue = parts.join('');
const result = `<ul>${innerValue}</ul>`;
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<li>${item}</li>`);
}
const innerValue = parts.join(''); // разделителя нет
// Правильно
const parts = [];
for (const item of coll) {
parts.push(`<li>${item}</li>`);
}
const innerValue = parts.join('\n'); // перевод строки
Дополнительные материалы

Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Урок «Как эффективно учиться на Хекслете»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.