ES6: литералы шаблона изнутри
Перевод статьи ES6 Template Literals in Depth с сайта ponyfoo.com, опубликовано на css-live.ru с разрешения автора — Николаса Беваквы.
Вчера мы рассмотрели «Деструктурирование в JavaScript (ES6) изнутри», и некоторые наиболее распространённые примеры его использования. Сегодня мы познакомимся с литералами шаблона. Что они из себя представляют, как их использовать и для чего.
Литералы шаблона — новая возможность в ES6 для более удобной работы со строками и шаблонами строки. Оберните текст в `обратные кавычки`
и получите описанные ниже возможности.
- В них можно интерполировать переменные
- На самом деле можно интерполировать строки с любыми выражениями, не только переменные
- Они могут быть многострочными. Наконец-то!
- Можно создавать необработанные шаблоны, которые не интерпретируют обратные кавычки
В добавок есть возможность определить метод, который решит, как поступить с шаблоном, кроме как задействовать обычное шаблонное поведение. У этого метода есть несколько интересных практических применений.
Давайте углубимся в литералы шаблона и посмотрим, что можно придумать.
Применение литералов шаблона
Мы уже рассматривали базовый пример `Я обычная строка`
. Первый плюс литералов шаблона, который стоит отметить — теперь можно объявлять строки с любыми кавычками, и одинарными, и двойными, и при этом не надо ничего экранировать
var text = `Ого, сколько разных кавычек, и прямо "из коробки"! Я прямо как д'Артаньян!`
Это конечно здорово, но литералы шаблона способны и на большее. Как насчёт реальной интерполяции? Для этого можно использовать нотацию ${expression}
.
var host = 'ponyfoo.com' var text = `этот блог живёт на ${host}` console.log(text) // <- 'этот блог живёт на ponyfoo.com'
Выше я заметил, что на месте ${host} могут быть какие угодно выражения. Представляйте себе, что какое бы выражение вы ни вставляли, переменная объявляется еще до запуска шаблона, а потом это значение конкатенирует с остальной строкой. Поэтому все используемые переменные, вызываемые методы и т.д. должны находиться в текущей области видимости.
Все следующие выражения также работали бы. Теперь решение насчёт того, сколько логики вкладывать в интерполяцию выражений, зависит от нас.
var text = `этот блог живёт на ${'ponyfoo.com'}` console.log(text) // <- 'этот блог живёт на ponyfoo.com' var today = new Date() var text = `время и дата ${today.toLocaleString()}` console.log(text) // <- 'время и дата 8/26/2015, 3:15:20 PM' import moment from 'moment' var today = new Date() var text = `сегодня ${moment(today).format('Do [of] MMMM')}` console.log(text) // <- 'сегодня 26 августа' var text = `Такое вот неопределенное выражение ${Infinity/0}` console.log(text) // <- 'Такое вот неопределенное выражение'
Многострочные шаблоны означают, что вам больше уже не понадобятся следующие методы:
var text = ( 'foo\n' + 'bar\n' + 'baz' ) var text = [ 'foo', 'bar', 'baz' ].join('\n')
Теперь вместо этого можно просто использовать обратные кавычки! Заметьте, что пробелы имеют значение, так что вам по-прежнему могут понадобиться скобки, чтобы отделить первую строку текста от объявления переменной.
var text = ( `foo bar baz`)
Многострочные шаблоны проявляют себя во всей красе, когда, к примеру, у нас есть кусок HTML, в который нам хочется интерполировать переменные. Подобно JSX, можно воспользоваться выражением для перебора коллекции и вернуть в return
другой литерал шаблона, чтобы объявить элементы списка. Это очень удобно для объявления субкомпонентов в ваших шаблонах. Обратите также внимание, как я использую деструктирование, чтобы не писать article
перед каждым своим выражением — хорошо, когда есть «этакий блок with
, только нормальный»
var article = { title: 'Привет, литералы шаблона',, teaser: 'Интерполяция строки прекрасна. Вот некоторые возможности',, body: 'Куча HTML без опасных символов', tags: ['es6', 'template-literals', 'es6-in-depth'] } var {title,teaser,body,tags} = article var html = `<article> <header> <h1>${title}</h1> </header> <section> <div>${teaser}</div> <div>${body}</div> </section> <footer> <ul> ${tags.map(tag => `<li>${tag}</li>`).join('\n ')} </ul> </footer> </article>`
Результат этого кода показан ниже. Заметьте, что пары пробелов хватило, чтобы у <li>
получились нормальные отступы.
<article> <header> <h1>Привет, литералы шаблона</h1> </header> <section> <div>Интерполяция строки прекрасна. Вот некоторые возможности</div> <div>Куча HTML без опасных символов</div> </section> <footer> <ul> <li>es6</li> <li>template-literals</li> <li>es6-in-depth</li> </ul> </footer> </article>
Необработанные шаблоны по сути то же самое, необходимо просто дописать перед литералом шаблона с String.raw
. В некоторых случаях это может быть очень удобно.
var text = String.raw `Символ новой строки "\n" не превратится в новую строку. Он будет экранирован.` console.log(text) // "\n" новая строка не станет новой. // Он будет экранирован.
Возможно вы заметили, что String.raw
кажутся особой частью синтаксиса литерала шаблона, и вы правы! Выбранный вами метод будет применяться для парсинга шаблона. Методы литерала шаблона — называемые «Помеченными шаблонами» — принимают массив, содержащий список статичных частей шаблона, а также каждое выражение в их собственных переменных.
Например, литерал шаблона `Привет, ${name}. Я ${emotion}!`
передаст аргументы «помеченного шаблона» в вызов функции, как показано ниже
fn([‘Привет, ‘, ‘. Я ‘, ‘!’], ‘nico’, ‘озадачен
Порядок, в котором идут аргументы, может показаться странным, но всё становится на свои места, если взглянуть на это так: для каждого элемента в массиве шаблона (кроме последнего — прим. перев.) есть результат выражения после него.
О помеченных шаблонах понятно
Ниже я привёл пример normal
ного метода, который работает точно так же, как и поведение по умолчанию. Это поможет лучше понять, что происходит под капотом шаблонных литералов.
Если вы не знаете, что делает
.reduce
, зайдите на MDN или в мою статью «Развлечения с настоящими массивами». Этот метод бывает очень кстати, когда нужно свести коллекцию к одному значению, вычисляемому на ее основе.
В данном случае можно сокращать шаблон template
, начиная от template[0]
, и затем сокращая все другие части, добавляя предыдущее выражение expression
и последующую часть part
.
function normal (template, ...expressions) { return template.reduce((accumulator, part, i) => { return accumulator + expressions[i - 1] + part }) }
Синтаксис ...expressions
— также новинка ES6. Он называется «синтаксисом оставшихся параметров», и фактически кладёт все аргументы, переданные в normal
после template
, в один массив. Попробуйте нижеприведённый помеченный шаблон, и вы заметите, что получите тот же результат, что и при опущенном normal
.
var name = 'nico' var outfit = 'пальто' var text = normal`привет ${name}, ты сегодня выглядишь потрясающе в этом ${outfit}` console.log(text) // <- 'привет, nico, ты сегодня выглядишь потрясающе в этом пальто'
Выяснив, как работают помеченные шаблоны, что мы можем с ними делать? Ну, что захотим. Например, можно вывести полученные данные заглавными буквами, придав нашему приветствию более сатирический вид — Я мысленно прочитал результат голосом фокусника Гоба из ситкома «Замедленное развитие», теперь вот сижу и ржу в голос, как псих. Эх, не надо было этого делать.
function upperExpr (template, ...expressions) { return template.reduce((accumulator, part, i) => { return accumulator + expressions[i - 1].toUpperCase() + part }) } var name = 'nico' var outfit = 'пальто' var text = upperExpr`привет ${name}, ты сегодня выглядишь потрясающе в этом ${outfit}` console.log(text) // <- 'привет, NICO, ты сегодня выглядишь потрясающе в этом ПАЛЬТО'
Очевидно, помеченные шаблоны можно использовать не только для приколов над самим собой, но и для чего-то более полезного. По сути, от них можно с ума сойти. Определенно полезным применением было бы автоматически очищать пользовательские данные в ваших шаблонах. Если у нас есть шаблон, в котором все выражения рассматриваются как полученные данные, мы могли бы использовать insane
, чтобы очистить выражения от ненужных тегов HTML.
import insane from 'insane' function sanitize (template, ...expressions) { return template.reduce((accumulator, part, i) => { return accumulator + insane(expressions[i - 1]) + part }) } var comment = 'хаха, xss — это так легко< iframe src="http://evil.corp">< /iframe>' var html = sanitize`< div>${comment}< /div>` console.log(html) // < - '< div>хаха, xss — это так легко< /div>'
Теперь не так легко!
Когда-нибудь я наверняка буду начинать и заканчивать абсолютно все строки в JavaScript исключительно обратными кавычками.
P.S. Это тоже может быть интересно:
Привет!
Поправьте шаблон функции fn([Привет, ‘, ‘. Я ‘, ‘!’], ‘nico’, ‘озадачен) на fn([‘Привет, ‘, ‘. Я ‘, ‘!’], ‘nico’, ‘озадачен)