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

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

У попередніх версіях специфікації 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.

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

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
Template literalsChrome Full support 41Edge Full support 12Firefox Full support 34IE No support NoOpera Full support 28Safari Full support 9WebView Android Full support 41Chrome Android Full support 41Firefox Android Full support 34Opera Android Full support 28Safari iOS Full support 9Samsung Internet Android Full support 4.0nodejs Full support 4.0.0
Escape sequences allowed in tagged template literals
Experimental
Chrome Full support 62Edge Full support 79Firefox Full support 53IE No support NoOpera Full support 49Safari Full support 11WebView Android Full support 62Chrome Android Full support 62Firefox Android Full support 53Opera Android Full support 46Safari iOS Full support 11Samsung Internet Android Full support 8.0nodejs Full support 8.10.0

Legend

Full support  
Full support
No support  
No support
Experimental. Expect behavior to change in the future.
Experimental. Expect behavior to change in the future.

Див. також