Шаблонные строки

Шаблонными литералами называются строковые литералы, допускающие использование выражений внутри. С ними вы можете использовать многострочные литералы и строковую интерполяцию. В спецификациях до ES2015 они назывались "шаблонными строками".

Синтаксис

`строка текста`

`строка текста 1
 строка текста 2`

`строка текста ${выражение} строка текста`

tag `строка текста ${выражение} строка текста`

Описание

Шаблонные литералы заключены в обратные кавычки (` `) вместо двойных или одинарных. Они могут содержать подстановки, обозначаемые знаком доллара и фигурными скобками (${выражение}). Выражения в подстановках и текст между ними передаются в функцию. По умолчанию функция просто объединяет все части в строку. Если перед строкой есть выражение (здесь это tag), то шаблонная строка называется "теговым шаблоном". В этом случае, теговое выражение (обычно функция) вызывается с обработанным шаблонным литералом, который вы можете изменить перед выводом. Для экранирования обратной кавычки в шаблонных литералах указывается обратный слеш \.

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

Многострочные литералы

Символы новой строки являются частью шаблонных литералов. Используя обычные строки, вставка переноса потребовала бы следующего синтаксиса:

console.log('string text line 1\n' +
'string text line 2');
// "string text line 1
//  string text line 2"

То же с использованием шаблонных литералов:

console.log(`string text line 1
string text line 2`);
// "string text line 1
//  string text line 2"

Интерполяция выражений

Для вставки выражений в обычные строки вам пришлось бы использовать следующий синтаксис:

var a = 5;
var b = 10;
console.log('Fifteen is ' + (a + b) + ' and not ' + (2 * a + b) + '.');
// "Fifteen is 15 and not 20."

Теперь, при помощи шаблонных литералов, вам доступен "синтаксический сахар", делающий подстановки вроде той более читабельными:

var a = 5;
var b = 10;
console.log(`Fifteen is ${a + b} and not ${2 * a + b}.`);
// "Fifteen is 15 and not 20."

Вложенные шаблоны

Временами, вложить шаблон — это кратчайший и, возможно, более читабельный способ составить строку. Просто поместите внутрь шаблона с обратными кавычками ещё одни, обернув их в подстановку ${ }. Например, если выражение истинно, можно вернуть шаблонный литерал.

В ES5:

var 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'}` }`;

Теговые шаблоны

Расширенной формой шаблонных литералов являются теговые шаблоны. Они позволяют разбирать шаблонные литералы с помощью функции. Первый аргумент такой функции содержит массив строковых значений, а остальные содержат выражения из подстановок. В итоге, функция должна вернуть собранную строку (или что-либо совсем иное, как будет показано далее). Имя функции может быть любым.

var person = 'Mike';
var age = 28;

function myTag(strings, personExp, ageExp) {
  var str0 = strings[0]; // "That "
  var str1 = strings[1]; // " is a "

  // Технически, в конце итогового выражения
  // (в нашем примере) есть ещё одна строка,
  // но она пустая (""), так что пропустим её.
  // var str2 = strings[2];

  var ageStr;
  if (ageExp > 99){
    ageStr = 'centenarian';
  } else {
    ageStr = 'youngster';
  }

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

var output = myTag`That ${ person } is a ${ age }`;

console.log(output);
// That Mike is a youngster

Функция тега не обязана возвращать строку, как показано в примере ниже:

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

var t1Closure = template`${0}${1}${0}!`;
t1Closure('Y', 'A');  // "YAY!"
var t2Closure = template`${0} ${'foo'}!`;
t2Closure('Hello', {foo: 'World'});  // "Hello World!"

Сырые строки

Специальное свойство raw, доступное для первого аргумента тегового шаблона, позволяет получить строку в том виде, в каком она была введена, без экранирования.

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

tag`string text line 1 \\n string text line 2`;
// выводит "string text line 1 \\n string text line 2",
// включая 'n' и два символа '\'

Вдобавок, существует метод String.raw(), возвращающий точно такую же исходную строку, какую вернула бы функция шаблона по умолчанию и строковая конкатенация вместе.

var str = String.raw`Hi\n${2+3}!`;
// "Hi\n5!"

str.length;
// 6

str.split('').join(',');
// "H,i,\,n,5,!"

Теговые шаблоны и экранирование символов

Поведение в ES2016

В ECMAScript 2016 теговые шаблоны следуют правилам экранирования следующих символов:

  • символы Unicode, начинающиеся с "\u", например, \u00A9
  • точки кода Unicode, начинающиеся с "\u{}", например, \u{2F804}
  • шестнадцатеричные представления символов, начинающиеся с "\x", например, \xA9
  • восьмеричные представления символов, начинающиеся с "\", например, \251​​​​​​

Отсюда вытекает проблема теговых шаблонов: следуя грамматике ECMAScript, анализатор кода, найдя символ \, будет искать корректное представление символа Unicode, но может не найти его вовсе. Пример ниже показывает это:

latex`\unicode`
// В старых версиях ECMAScript (ES2016 и раньше) выкинет исключение:
// SyntaxError: malformed Unicode character escape sequence

Поведение в ES2018

Теговые шаблоны должны позволять встраивать языки (например, DSLs или LaTeX), в которых широко используются многие другие экранирования. Предложение Редакция шаблонных литералов (уровень 4, одобренный к добавлению в стандарт ECMAScript 2018) устраняет синтаксические ограничения экранирования теговых шаблонов в ECMAScript.

Однако, некорректное экранирование символов по-прежнему нужно отображать в "приготовленном" отображении. Оно показывается в виде undefined в "приготовленном" массиве:

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

latex`\unicode`

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

Заметьте, что ограничение на экранирование символов проявляется лишь в теговых шаблонах, и не проявляется в нетеговых шаблонных литералах:

let bad = `bad escape sequence: \unicode`;

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

Спецификация Статус Комментарий
ECMAScript 2015 (6th Edition, ECMA-262)
Определение 'Template Literals' в этой спецификации.
Стандарт Изначальное определение. Определено в секциях Template Literals, Tagged Templates
ECMAScript (ECMA-262)
Определение 'Template Literals' в этой спецификации.
Живой стандарт Определено в секциях Template Literals, Tagged Templates
Template Literal Revision Черновик 4-го уровня Устранено ограничение экранирования в теговых шаблонах

Совместимость с браузерами

BCD tables only load in the browser

Смотрите также