Получение буквального значения со строками шаблона ES6

Адди Османи
Addy Osmani

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

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

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

Синтаксис

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

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», а комментарий — «& — забавный тег», мы должны вывести:

<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 beta+, IE Tech Preview, Firefox 35+ и io.js. На практике, если вы хотите использовать их в производстве сегодня, они поддерживаются основными транспиляторами ES6, включая Traceur и 6to5. Если вы хотите опробовать их, ознакомьтесь с нашим примером строк шаблона в репозитории образцов Chrome. Вас также могут заинтересовать эквиваленты ES6 в ES5 , в которых показано, как добиться некоторых преимуществ строк шаблонов, которые можно получить с помощью ES5 сегодня.

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

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

Дальнейшее чтение