Como conseguir o literal com strings de modelo ES6

Addy Osmani
Addy Osmani

Historicamente, as strings em JavaScript são limitadas e não têm os recursos esperados em linguagens como Python ou Ruby. As strings de modelo do ES6 (disponíveis no Chrome 41 e versões posteriores) mudam isso fundamentalmente. Eles apresentam uma maneira de definir strings com linguagens específicas de domínio (DSLs), trazendo melhores:

  • Interpolação de strings
  • Expressões incorporadas
  • Strings de várias linhas sem invasão
  • Formatação de strings
  • Codificação de string para escape de HTML seguro, localização e muito mais.

Em vez de inserir mais um recurso em strings como as conhecemos hoje, as strings de modelo apresentam uma maneira completamente diferente de resolver esses problemas.

Sintaxe

As strings de modelo usam acentos graves (``) em vez das aspas simples ou duplas que estamos acostumados a usar nas strings regulares. Assim, uma string de modelo poderia ser escrita desta forma:

var greeting = `Yo World!`;

Até agora, as strings de modelo não nos deram nada além das strings normais. Vamos mudar isso.

Substituição de string

Um dos primeiros benefícios reais é a substituição de strings. A substituição nos permite usar qualquer expressão JavaScript válida (incluindo digamos, a adição de variáveis) e, dentro de um literal de modelo, o resultado será gerado como parte da mesma string.

As strings de modelo podem conter marcadores de posição para substituição de strings usando a sintaxe ${ }, conforme demonstrado abaixo:

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

// => "Yo, Brendan!"

Como todas as substituições de string em strings de modelo são expressões JavaScript, podemos substituir muito mais do que nomes de variáveis. Por exemplo, abaixo, podemos usar a interpolação de expressões para incorporar uma matemática inline legível:

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.

Elas também são muito úteis para funções dentro de expressões:

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

O ${} funciona bem com qualquer tipo de expressão, incluindo expressões de membro e chamadas de método:

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

Se você precisar de crases dentro da string, poderá fazer escape usando o caractere de barra invertida \ da seguinte maneira:

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

Strings de várias linhas

Há algum tempo, as strings de várias linhas em JavaScript exigiam soluções alternativas hackeadas. As soluções atuais para eles exigem que strings existam em uma única linha ou sejam divididas em strings de várias linhas usando uma \ (barra invertida) antes de cada nova linha. Exemplo:

var greeting = "Yo \
World";

Embora isso funcione bem na maioria dos mecanismos JavaScript modernos, o comportamento em si ainda é um pouco "hack". Também é possível usar a concatenação de strings para simular um suporte a várias linhas, mas isso também deixa algo a ser desejado:

var greeting = "Yo " +
"World";

As strings de modelo simplificam significativamente as strings de várias linhas. Basta incluir novas linhas onde forem necessárias e BOOM. Confira um exemplo:

Qualquer espaço em branco dentro da sintaxe de acento grave também será considerado parte da string.

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

Modelos marcados

Até agora, vimos o uso de strings de modelo para substituição de strings e para criação de strings de várias linhas. Outro recurso eficiente que eles oferecem são os modelos de tags. Os modelos marcados transformam uma string de modelo colocando um nome de função antes dela. Exemplo:

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

A semântica de uma string de modelo com tag é muito diferente daquela de uma string normal. Em essência, eles são um tipo especial de chamada de função: os "simplificados" acima em

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

Observe como o argumento (n + 1) corresponde à substituição que ocorre entre a enésima e (n + 1) entradas na matriz de strings. Isso pode ser útil para todos os tipos de coisas, mas uma das mais simples é o escape automático de qualquer variável interpolada.

Por exemplo, é possível escrever uma função de escape HTML de forma que...

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

retorna uma string com as variáveis apropriadas substituídas, mas com todos os caracteres não seguros de HTML substituídos. Vamos fazer isso. Nossa função de escape HTML terá dois argumentos: um nome de usuário e um comentário. Os dois podem conter caracteres HTML não seguros (ou seja, ', ", <, > e &). Por exemplo, se o nome de usuário for "Domenic Denicola" e o comentário for "& é uma tag divertida", a saída será:

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

Nossa solução de modelo com tag poderia ser escrita desta forma:

// 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"

Outros usos possíveis incluem escape automático, formatação, localização e, em geral, substituições mais complexas:

// 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}`;

Resumo

As strings de modelo estão no Chrome 41 Beta+, no IE Tech Preview, no Firefox 35+ e no io.js. Na prática, se você quer usá-los na produção hoje, eles são compatíveis com os principais transpiladores ES6, incluindo o Traceur e o 6to5. Se quiser, confira nosso exemplo de strings de modelo (link em inglês) no repositório de exemplos do Chrome. Talvez você também tenha interesse no artigo Equivalentes do ES6 no ES5, que demonstra como realizar algumas das strings de modelo de simplificação trazidas usando o ES5 atualmente.

As strings de modelo trazem muitos recursos importantes para o JavaScript. Isso inclui maneiras melhores de fazer interpolação de strings e expressões, strings de várias linhas e a capacidade de criar suas próprias DSLs.

Um dos recursos mais importantes que ela oferece são os modelos de tags, que são essenciais para a criação de DSLs. Eles recebem as partes de uma string de modelo como argumentos, e você pode decidir como usar as strings e substituições para determinar o resultado final da string.

Leitura complementar