Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Шаблонні рядки в JavaScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Шаблонні рядки (template literals)** - це рядки у зворотних лапках (`` ` ``), які підтримують вбудовані вирази через `${}`, багаторядковий текст і теговані шаблони. З'явились в ES6. ```javascript const name = "Alice"; const greeting = `Hello, ${name}!`; // "Hello, Alice!" ``` **Ключове:** використовуй коли рядок містить змінні або займає кілька рядків.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Шаблонні рядки (template literals)** - це рядки у зворотних лапках (`` ` ``), які дозволяють вставляти вирази через синтаксис `${}`, писати багаторядковий текст без escape-послідовностей і використовувати теговані шаблони. З'явились в ES6. ## Теорія ### TL;DR - Шаблонні рядки - як mail merge: пишеш шаблон один раз, вставляєш дані де треба - Головна різниця: зворотні лапки + `${}` замість конкатенації через `+` - Є змінні або багаторядковий текст - бери шаблонний рядок - Статичний текст без змінних - звичайні лапки читаються простіше - В `${}` можна писати будь-який вираз: математику, виклики функцій, тернарний оператор ### Швидкий приклад ```javascript const name = "Alice"; const age = 28; // Старий спосіб const old = "Hello, " + name + "! You are " + age + " years old."; // Шаблонний рядок const modern = `Hello, ${name}! You are ${age} years old.`; // "Hello, Alice! You are 28 years old." // Багаторядковий текст без \n const message = `Welcome, ${name}! Your account is active. Age: ${age}`; ``` Синтаксис із зворотними лапками прибирає потребу в `+` між кожним шматком рядка і змінною. ### Головна різниця Шаблонні рядки обчислюють вирази в `${}` під час виконання і автоматично перетворюють результат на рядок. При конкатенації треба ланцюжком з'єднувати шматки через `+`, а для переносів рядка додавати `\n`. Шаблонні рядки вирішують обидві проблеми одразу. І тільки вони відкривають теговані шаблони (tagged templates) - коли функція отримує статичні частини рядка і значення виразів окремо, до того як рядок зібрано. Зі звичайними лапками це неможливо. ### Коли використовувати - Будь-який рядок де є змінні - Багаторядковий контент: HTML-фрагменти, SQL-запити, повідомлення про помилки - Складні вирази: `${user.isAdmin ? 'admin' : 'user'}` - Динамічні атрибути і class-імена в компонентах - Уникай для статичних рядків без змінних - там звичайні лапки читаються краще На практиці, коли звикаєш до шаблонних рядків, конкатенація починає дратувати навіть у простих двочастинних рядках. ### Як це працює всередині Коли JS-рушій зустрічає шаблонний рядок, він розбирає текст між зворотними лапками і знаходить кожен блок `${}`. Кожен вираз обчислюється в поточному scope, перетворюється через `.toString()` і вставляється на своє місце. З тегованими шаблонами рушій розділяє статичні частини рядка і значення виразів на окремі аргументи, передає їх у тег-функцію - і вже функція вирішує як їх об'єднати. Структура розбирається під час парсингу, обчислення виразів відбувається під час виконання. ### Типові помилки **Забули `$` перед `{}`** ```javascript const name = "Alice"; // ❌ Виведе літеральний текст const greeting = `Hello, {name}!`; console.log(greeting); // "Hello, {name}!" - фігурні дужки без $ нічого не роблять // ✅ Правильно const greeting2 = `Hello, ${name}!`; console.log(greeting2); // "Hello, Alice!" ``` **Шаблонний рядок як ключ об'єкта** ```javascript const key = "user"; // ❌ Ключ стає літеральним рядком "${key}_name" // Без квадратних дужок: { '${key}_name': 'Alice' } - неправильно // ✅ Обчислювані властивості з квадратними дужками const obj = { [`${key}_name`]: "Alice" }; console.log(obj); // { user_name: 'Alice' } ``` **Небезпечний ввід користувача і XSS** ```javascript const userInput = "<img src=x onerror='alert(1)'>"; // ❌ Скрипт виконається при рендері const html = `<div>${userInput}</div>`; // ✅ Екрануй перед вставкою function escapeHtml(text) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, m => map[m]); } const safeHtml = `<div>${escapeHtml(userInput)}</div>`; // <div><img src=x onerror='alert(1)'></div> - безпечно ``` **Зайві пробіли і переноси в багаторядкових шаблонах** ```javascript // ❌ Перед <div> буде перенос рядка і відступи const html = ` <div> <p>Content</p> </div> `; // ✅ Використовуй .trim() або уникай початкового переносу const html2 = `<div> <p>Content</p> </div>`.trim(); ``` ### Де зустрічається в реальних проектах - React: динамічні `className` і `aria-label` атрибути - Express/Node.js: SQL-запити, log-повідомлення, відповіді з помилками - Apollo Client: GraphQL-запити через тег `gql` - styled-components: CSS-in-JS через синтаксис тегованих шаблонів - Jest: опис тестів і snapshot-рядки ### Питання на співбесіді **Q:** Що буде якщо вставити об'єкт у `${}`? **A:** Викличеться `.toString()`, і ти отримаєш `[object Object]`. Якщо потрібен читабельний вивід - використовуй `JSON.stringify(obj)`. **Q:** Чи можна використовувати шаблонний рядок як ключ об'єкта? **A:** Напряму - ні. Без квадратних дужок зворотні лапки розглядаються як літеральний ключ. Потрібен синтаксис обчислюваних властивостей: `{ [\`key_${x}\`]: value }`. **Q:** Як працюють теговані шаблони (tagged templates)? **A:** Тег - це функція що отримує масив статичних частин рядка і значення виразів як окремі аргументи: `(strings, ...values)`. Функція сама вирішує як їх об'єднати. Саме так sql-теги екранують параметри запитів, а styled-components обробляє CSS-рядки. **Q:** Чи повільніші шаблонні рядки порівняно з конкатенацією? **A:** Ні. Сучасні рушії V8 і SpiderMonkey оптимізують обидва підходи. Різниця в продуктивності несуттєва. Причина обирати шаблонні рядки - читабельність. **Q:** (Senior-рівень) Як написати тегований шаблон що автоматично захищає від XSS? **A:** Пиши тег-функцію яка проходить по масиву `values` і екранує кожне значення перед вставкою. Функція отримує `(strings, ...values)` - пройди по `values` з функцією екранування і чергуй з елементами `strings`. Саме так працюють бібліотеки `lit-html` і `htm`. ## Приклади ### Базова інтерполяція і багаторядковий рядок ```javascript const user = { name: "Alice", email: "alice@example.com" }; const isActive = true; const card = ` Name: ${user.name} Email: ${user.email} Status: ${isActive ? "active" : "inactive"} Joined: ${new Date().toLocaleDateString()} `.trim(); console.log(card); // Name: Alice // Email: alice@example.com // Status: active // Joined: 15.01.2025 ``` Тернарні оператори, виклики методів і звернення до властивостей - все це працює всередині `${}`. `.trim()` на кінці прибирає початковий і кінцевий переноси рядка від зворотних лапок. ### Тегований шаблон для безпечних SQL-запитів ```javascript function sql(strings, ...values) { const escaped = values.map(v => typeof v === "string" ? `'${v.replace(/'/g, "''")}'` : v ); let result = strings[0]; for (let i = 0; i < escaped.length; i++) { result += escaped[i] + strings[i + 1]; } return result; } const userId = 42; const userName = "O'Brien"; // містить апостроф const query = sql`SELECT * FROM users WHERE id = ${userId} AND name = ${userName}`; console.log(query); // SELECT * FROM users WHERE id = 42 AND name = 'O''Brien' // Апостроф екранований, SQL-ін'єкція неможлива ``` Тег-функція запускається до того як рядок збирається. Вона отримує статичні частини в `strings` і обчислені вирази в `values`. Це той самий патерн що використовує Apollo Client для GraphQL-запитів через тег `gql`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.