Дословное использование строк шаблонов ES6

Строки в JavaScript всегда были ограничены и не имели возможностей, которые можно было бы ожидать от таких языков, как Python или Ruby. Шаблонные строки ES6 (доступны в Chrome 41 и более поздних версиях) в корне меняют ситуацию. Они позволяют определять строки с помощью языков, ориентированных на определенные области (DSL), и обеспечивают:

  • Интерполяция строк
  • Встроенные выражения
  • Многострочные строки без хаков
  • Форматирование строк
  • Тегирование строк для безопасного экранирования HTML, локализации и т. д.

Вместо того чтобы добавлять в строки ещё одну функцию, мы предлагаем совершенно другой способ решения этих проблем.

Синтаксис

В строках шаблонов используются обратные кавычки (``), а не одинарные или двойные кавычки, как в обычных строках. Таким образом, строка шаблона может выглядеть следующим образом:

var greeting = `Yo World!`;

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

Замена строк

Одно из первых преимуществ регулярных выражений – возможность замены строк. Подстановка позволяет использовать любое допустимое выражение JavaScript (в том числе сложение переменных) и вставлять результат в шаблонную строку.

В строках шаблонов можно использовать плейсхолдеры для замены строк с помощью синтаксиса ${ }, как показано ниже:

// Simple string substitution
var name = "Brendan";
console.log(`Yo, ${name}!`);

// => "Yo, Brendan!"

Поскольку все подстановки строк в шаблонных строках являются выражениями JavaScript, мы можем подставлять не только имена переменных. Например, ниже мы можем использовать интерполяцию выражений, чтобы встроить в текст читаемую математическую формулу:

var a = 10;
var b = 10;
console.log(`JavaScript first appeared ${a+b} years ago. Wow!`);

//=> JavaScript first appeared 20 years ago. Wow!

console.log(`The number of JS MVC frameworks is ${2 * (a + b)} and not ${10 * (a + b)}.`);
//=> The number of JS frameworks is 40 and not 200.

Они также очень полезны для функций внутри выражений:

function fn() { return "I am a result. Rarr"; }
console.log(`foo ${fn()} bar`);
//=> foo I am a result. Rarr bar.

Оператор ${} можно использовать с любыми выражениями, в том числе с выражениями членов и вызовами методов:

var user = {name: 'Caitlin Potter'};
console.log(`Thanks for getting this into V8, ${user.name.toUpperCase()}.`);

// => "Thanks for getting this into V8, CAITLIN POTTER";

// And another example
var thing = 'template strings';
console.log(`Say hello to ${thing}.`);

// => Say hello to template strings

Если вам нужно использовать обратные кавычки в строке, их можно экранировать с помощью обратной косой черты \, как показано ниже:

var greeting = `\`Yo\` World!`;

Многострочные строки

В JavaScript уже давно для многострочных строк требуются обходные пути. В текущих решениях строки должны быть либо однострочными, либо многострочными, но с символом \ (обратная косая черта) перед каждым переносом строки. Пример:

var greeting = "Yo \
World";

Хотя это должно работать в большинстве современных движков JavaScript, само поведение все ещё немного похоже на хак. Для имитации многострочного текста можно использовать конкатенацию строк, но это тоже не лучший вариант:

var greeting = "Yo " +
"World";

Шаблонные строки значительно упрощают многострочные строки. Просто добавьте в текст переносы строк, где это необходимо, и все готово. Пример:

Любые пробелы внутри синтаксиса обратных кавычек также будут считаться частью строки.

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

Шаблоны с тегами

До сих пор мы рассматривали использование строк шаблонов для подстановки строк и создания многострочных строк. Ещё одна полезная функция – шаблоны с тегами. Шаблоны с тегами преобразуют строку шаблона, добавляя перед ней название функции. Пример:

fn`Hello ${you}! You're looking ${adjective} today!`

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

fn(["Hello ", "! You're looking ", " today!"], you, adjective);

Обратите внимание, что (n + 1)-й аргумент соответствует замене, которая происходит между n-й и (n + 1)-й записями в массиве строк. Это может быть полезно для самых разных целей, но одна из самых простых – автоматическое экранирование любых интерполированных переменных.

Например, можно написать функцию, которая будет экранировать HTML-код следующим образом:

html`<p title="${title}">Hello ${you}!</p>`

возвращает строку с подставленными переменными, но все небезопасные символы HTML заменены. Давайте сделаем это. Наша функция экранирования HTML будет принимать два аргумента: имя пользователя и комментарий. Оба могут содержать небезопасные для HTML символы (а именно: ", <, > и &). Например, если имя пользователя – Domenic Denicola, а комментарий – & is a fun tag, то мы должны вывести:

<b>Domenic Denicola says:</b> "&amp; is a fun tag"

Решение на основе шаблонов с тегами можно записать следующим образом:

// HTML Escape helper utility
var util = (function () {
    // Thanks to Andrea Giammarchi
    var
    reEscape = /[&<>'"]/g,
    reUnescape = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g,
    oEscape = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        "'": '&#39;',
        '"': '&quot;'
    },
    oUnescape = {
        '&amp;': '&',
        '&#38;': '&',
        '&lt;': '<',
        '&#60;': '<',
        '&gt;': '>',
        '&#62;': '>',
        '&apos;': "'",
        '&#39;': "'",
        '&quot;': '"',
        '&#34;': '"'
    },
    fnEscape = function (m) {
        return oEscape[m];
    },
    fnUnescape = function (m) {
        return oUnescape[m];
    },
    replace = String.prototype.replace
    ;
    return (Object.freeze || Object)({
    escape: function escape(s) {
        return replace.call(s, reEscape, fnEscape);
    },
    unescape: function unescape(s) {
        return replace.call(s, reUnescape, fnUnescape);
    }
    });
}());

// Tagged template function
function html(pieces) {
    var result = pieces[0];
    var substitutions = [].slice.call(arguments, 1);
    for (var i = 0; i < substitutions.length; ++i) {
        result += util.escape(substitutions[i]) + pieces[i + 1];
    }

    return result;
}

var username = "Domenic Denicola";
var tag = "& is a fun tag";
console.log(html`<b>${username} says</b>: "${tag}"`);
//=> <b>Domenic Denicola says</b>: "&amp; is a fun tag"

Другие возможные варианты использования включают автоматическое экранирование, форматирование, локализацию и в целом более сложные замены:

// Contextual auto-escaping
qsa`.${className}`;
safehtml`<a href="${url}?q=${query}" onclick="alert('${message}')" style="color: ${color}">${message}</a>`;

// Localization and formatting
l10n`Hello ${name}; you are visitor number ${visitor}:n! You have ${money}:c in your account!`

// Embedded HTML/XML
jsx`<a href="${url}">${text}</a>` // becomes React.DOM.a({ href: url }, text)

// DSLs for code execution
var childProcess = sh`ps ax | grep ${pid}`;

Сводка

Строки шаблонов доступны в бета-версии Chrome 41 и более поздних версиях, IE Tech Preview, Firefox 35 и более поздних версиях и io.js. Если вы хотите использовать их в производстве, то они поддерживаются основными трансляторами ES6, включая Traceur и 6to5. Если вы хотите попробовать их в действии, ознакомьтесь с нашим примером использования шаблонных строк в репозитории примеров Chrome. Также рекомендуем ознакомиться с эквивалентами ES6 в ES5, где показано, как реализовать некоторые возможности шаблонных строк с помощью ES5.

Шаблонные строки добавляют в JavaScript много важных возможностей. В частности, мы добавили более удобные способы интерполяции строк и выражений, многострочные строки и возможность создавать собственные DSL.

Одна из самых важных функций, которые они предоставляют, – это шаблоны с тегами, которые необходимы для создания таких DSL. Они получают части строки шаблона в качестве аргументов, и вы можете решить, как использовать строки и подстановки, чтобы определить окончательный вид строки.

Дополнительные материалы