Шаблонні літерали (шаблонні рядки)

Шаблонні літерали - це рядкові літерали, які дозволяють використання вбудованих виразів. З ними ви можете використовувати багаторядковий запис та засоби інтерполяції рядків.

У попередніх версіях специфікації ES2015 вони називались "шаблонними рядками".

Синтаксис

`текстовий рядок`

`текстовий рядок 1
 текстовий рядок 2`

`текст ${вираз} текст`

тег`текст ${вираз} текст`

Опис

Шаблонні літерали заключаються у зворотні лапки (` `) (гравіс) замість подвійних чи одинарних лапок.

Шаблонні літерали можуть містити заповнювачі. Вони позначаються знаком долара та фігурними дужками (${вираз}). Вирази у заповнювачах та текст між зворотними лапками (` `) передаються у функцію.

Проста функція просто з'єднує декілька частин у єдиний рядок. Якщо перед шаблоном знаходиться вираз (тег), він називається тегованим шаблоном. У цьому випадку вираз тегу (зазвичай, це функція) викликається разом із шаблонним літералом, і ви можете маніпулювати ним перед поверненням.

Щоб екранувати зворотні лапки у шаблонному літералі, поставте зворотній слеш (\) перед лапками.

`\`` === '`' // --> true

Багаторядковий запис

Будь-які символи нового рядка, вставлені у першокод, є частиною шаблонного літералу.

У звичайних рядках вам довелося б використати наступний синтаксис, щоб отримати багаторядковий запис:

console.log('текстовий рядок 1\n' +
'текстовий рядок 2');
// "текстовий рядок 1
// текстовий рядок 2"

У шаблонних літералах ви можете зробити те саме ось так:

console.log(`текстовий рядок 1
текстовий рядок 2`);
// "текстовий рядок 1
// текстовий рядок 2"

Інтерполяція виразів

Для того, щоб вставити вирази у звичайні рядки, ви б використали наступний синтаксис:

let a = 5;
let b = 10;
console.log('П\'ятнадцять - це ' + (a + b) + ', а\nне ' + (2 * a + b) + '.');
// "П'ятнадцять - це 15, а
// не 20."

Тепер, з шаблонними літералами, ви можете скористатись синтаксичним цукром, зробивши підстановки легшими для читання наступним чином:

let a = 5;
let b = 10;
console.log(`П'ятнадцять - це ${a + b}, а
не ${2 * a + b}.`);
// "П'ятнадцять - це 15, а
// не 20."

Вкладені шаблони

У певних випадках вкладений шаблон - це найкращий (і, мабуть, найлегший для читання) спосіб створювати рядки, що конфігуруються. Всередині шаблона у зворотних лапках легко створити внутрішні зворотні лапки, просто використавши їх всередині заповнювача ${ } у шаблоні.

Наприклад, якщо умова a дорівнює true, то повернути цей шаблонований літерал.

У ES5:

let classes = 'header';
classes += (isLargeScreen() ?
  '' : item.isCollapsed ?
    ' icon-expander' : ' icon-collapser');

У ES2015 з шаблонними літералами та без вкладення:

const classes = `header ${ isLargeScreen() ? '' :
  (item.isCollapsed ? 'icon-expander' : 'icon-collapser') }`;

У ES2015 з вкладеними шаблонними літералами:

const classes = `header ${ isLargeScreen() ? '' :
  `icon-${item.isCollapsed ? 'expander' : 'collapser'}` }`;

Теговані шаблони

Більш прогресивною формою шаблонних літералів є теговані шаблони.

Теги дозволяють розбирати шаблонні літерали за допомого функцій. Перший аргумент функції-тегу містить масив рядкових значень. Решта аргументів відносяться до виразів.

Далі функція-тег може виконати будь-які операції над цими аргументами та повернути отриманий рядок. (Як варіант, вона може повернути щось зовсім інше, як описано у одному з наступних прикладів.) 

Ім'я функції, що використовується для тегу, може бути яким завгодно.

let person = 'Майк';
let age = 28;

function myTag(strings, personExp, ageExp) {
  let str0 = strings[0]; // "Цей "
  let str1 = strings[1]; // " - "

  // Технічно, існує рядок після
  // останнього виразу (в нашому прикладі),
  // але він порожній (""), тому не зважаємо.
  // let str2 = strings[2];

  let ageStr;
  if (ageExp > 99){
    ageStr = 'довгожитель';
  } else {
    ageStr = 'юнак';
  }

  // Ми навіть можемо повернути рядок, побудований шаблонним літералом
  return `${str0}${personExp}${str1}${ageStr}`;
}

let output = myTag`Цей ${ person } - ${ age }`;

console.log(output);
// Цей Майк - юнак

Функція-тег може навіть не повертати рядок!

function template(strings, ...keys) {
  return (function(...values) {
    let dict = values[values.length - 1] || {};
    let result = [strings[0]];
    keys.forEach(function(key, i) {
      let value = Number.isInteger(key) ? values[key] : dict[key];
      result.push(value, strings[i + 1]);
    });
    return result.join('');
  });
}

let t1Closure = template`${0}${1}${0}!`;
t1Closure('Й', 'О');                      // "ЙОЙ!"
let t2Closure = template`${0} ${'foo'}!`;
t2Closure('Всім', {foo: 'привіт'});       // "Всім привіт!"

Сирі рядки

Спеціальна властивість raw, доступна на першому аргументі функції-тегу, дає доступ до сирих рядків у тому вигляді, як вони були записані, без обробки екранувань.

function tag(strings) {
  console.log(strings.raw[0]);
}

tag`текстовий рядок 1 \n текстовий рядок 2`;
// виводить "текстовий рядок 1 \n текстовий рядок 2" ,
// в тому числі два символи, '\' та 'n'

На додачу, існує метод String.raw() для створення сирих рядків — так само, як їх би створили проста шаблонна функція та об'єднання рядків.

let str = String.raw`Привіт\n${2+3}!`;
// "Привіт\n5!"

str.length;
// 10

Array.from(str).join(',');
// "П,р,и,в,і,т,\,n,5,!"

Теговані шаблони та екрановані послідовності

Поведінка в ES2016

У ECMAScript 2016 теговані шаблони відповідають правилам наступних екранованих послідовностей:

  • Послідовності Юнікоду починаються з "\u", наприклад, \u00A9
  • Екранування кодів символів Юнікоду позначаються через "\u{}", наприклад, \u{2F804}
  • Шістнадцяткові послідовності починаються з "\x", наприклад, \xA9
  • Вісімкові літеральні послідовності починаються з "\0o", далі йде одна чи більше цифр, наприклад, \0o251

Це означає, що тегований шаблон, наведений нижче, є проблематичним, оскільки, згідно з граматикою ECMAScript, синтаксичний аналізатор шукає коректну екрановану послідовність Юнікоду, але знаходить неправильно сформований синтаксис:

latex`\unicode`
// Викидає помилку у старших версіях ECMAScript (ES2016 та старші)
// SyntaxError: malformed Unicode character escape sequence

Перегляд недозволених екранованих послідовностей у ES2018

Теговані шаблони повинні дозволяти вкладені мови (наприклад, DSL чи LaTeX), де існують інші екрановані послідовності. Пропозиція ECMAScript Template Literal Revision (Стадія 4, має бути інтегрована у стандарт ECMAScript 2018) прибирає обмеження синтаксису екранованих послідовностей ECMAScript з тегованих шаблонів.

Однак, недозволені екрановані послідовності все одно мають бути представлені у “готовому” представленні. Вони з'являться як елемент undefined у масиві “cooked” (готовий):

function latex(str) {
  return { "cooked": str[0], "raw": str.raw[0] }
}

latex`\unicode`

// { cooked: undefined, raw: "\\unicode" }

Зауважте, що обмеження екранованих послідовностей прибираються лише з тегованих шаблонів, вони не прибираються з нетегованих шаблонних літералів:

let bad = `погана екранована послідовність: \unicode`;

Специфікації

Специфікація
ECMAScript (ECMA-262)
The definition of 'Template Literals' in that specification.
ECMAScript (ECMA-262)
The definition of 'Tagged templates Literals' in that specification.

Сумісність з веб-переглядачами

BCD tables only load in the browser

Див. також