Cómo obtener literales con strings de plantilla de ES6

Históricamente, las cadenas en JavaScript han sido limitadas y carecen de las capacidades que uno podría esperar de lenguajes como Python o Ruby. Las cadenas de plantilla de ES6 (disponibles en Chrome 41 y versiones posteriores) cambian eso de forma fundamental. Presentan una forma de definir cadenas con lenguajes específicos del dominio (DSL), lo que aporta las siguientes ventajas:

  • Interpolación de cadenas
  • Expresiones incorporadas
  • Cadenas de varias líneas sin trucos
  • Formato de cadenas
  • Etiquetado de cadenas para un escape seguro de HTML, localización y mucho más.

En lugar de agregar otra función a las cadenas tal como las conocemos hoy en día, las cadenas de plantilla introducen una forma completamente diferente de resolver estos problemas.

Sintaxis

Las cadenas de plantilla usan acentos graves (``) en lugar de las comillas simples o dobles que solemos usar con las cadenas normales. Por lo tanto, una cadena de plantilla se podría escribir de la siguiente manera:

var greeting = `Yo World!`;

Hasta ahora, las cadenas de plantilla no nos han proporcionado nada más que las cadenas normales. Cambiemos eso.

Sustitución de cadenas

Uno de sus primeros beneficios reales es la sustitución de cadenas. La sustitución nos permite tomar cualquier expresión de JavaScript válida (incluida, por ejemplo, la suma de variables) y, dentro de un literal de plantilla, el resultado se generará como parte de la misma cadena.

Las cadenas de plantilla pueden contener marcadores de posición para la sustitución de cadenas con la sintaxis ${ }, como se muestra a continuación:

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

// => "Yo, Brendan!"

Como todas las sustituciones de cadenas en las cadenas de plantilla son expresiones de JavaScript, podemos sustituir mucho más que nombres de variables. Por ejemplo, a continuación, podemos usar la interpolación de expresiones para incorporar algunas ecuaciones intercaladas legibles:

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.

También son muy útiles para las funciones dentro de las expresiones:

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

La ${} funciona bien con cualquier tipo de expresión, incluidas las expresiones de miembros y las llamadas 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

Si necesitas acentos graves dentro de tu cadena, puedes escaparlos con el carácter de barra inversa \ de la siguiente manera:

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

Cadenas de varias líneas

Las cadenas de varias líneas en JavaScript han requerido soluciones alternativas poco prácticas durante algún tiempo. Las soluciones actuales para ellos requieren que las cadenas existan en una sola línea o se dividan en cadenas de varias líneas con un \ (barra inversa) antes de cada salto de línea. Por ejemplo:

var greeting = "Yo \
World";

Si bien esto debería funcionar bien en la mayoría de los motores de JavaScript modernos, el comportamiento en sí sigue siendo un poco hack. También se puede usar la concatenación de cadenas para simular la compatibilidad con varias líneas, pero esto también deja algo que desear:

var greeting = "Yo " +
"World";

Las cadenas de plantilla simplifican significativamente las cadenas de varias líneas. Simplemente incluye saltos de línea donde sean necesarios y LISTO. Por ejemplo:

Cualquier espacio en blanco dentro de la sintaxis de comillas inversas también se considerará parte de la cadena.

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

Plantillas etiquetadas

Hasta ahora, vimos cómo usar cadenas de plantilla para la sustitución de cadenas y para crear cadenas de varias líneas. Otra función potente que ofrecen son las plantillas etiquetadas. Las plantillas etiquetadas transforman una cadena de plantilla colocando un nombre de función antes de la cadena de plantilla. Por ejemplo:

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

La semántica de una cadena de plantilla etiquetada es muy diferente de la de una normal. En esencia, son un tipo especial de llamada a función: lo anterior se "desazucara" en

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

Observa cómo el argumento (n + 1) corresponde a la sustitución que se produce entre la entrada n y la entrada (n + 1) en el array de cadenas. Esto puede ser útil para todo tipo de cosas, pero una de las más sencillas es el escape automático de cualquier variable interpolada.

Por ejemplo, podrías escribir una función de escape de HTML de modo que…

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

Devuelve una cadena con las variables adecuadas sustituidas, pero con todos los caracteres no seguros para HTML reemplazados. Hagámoslo. Nuestra función de escape de HTML tomará dos argumentos: un nombre de usuario y un comentario. Ambos pueden contener caracteres no seguros para HTML (específicamente, "'", """, "<", ">" y "&"). Por ejemplo, si el nombre de usuario es "Domenic Denicola" y el comentario es "& es una etiqueta divertida", deberíamos generar el siguiente resultado:

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

Por lo tanto, nuestra solución de plantillas etiquetadas se podría escribir de la siguiente manera:

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

Otros usos posibles incluyen el escape automático, el formato, la localización y, en general, sustituciones más complejas:

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

Resumen

Las cadenas de plantilla están disponibles en Chrome 41 beta y versiones posteriores, IE Tech Preview, Firefox 35 y versiones posteriores, y io.js. En la práctica, si quieres usarlos en producción hoy mismo, son compatibles con los principales transpiladores de ES6, incluidos Traceur y 6to5. Si quieres probarlas, consulta nuestro ejemplo de cadenas de plantillas en el repositorio de ejemplos de Chrome. También te puede interesar Equivalentes de ES6 en ES5, que muestra cómo lograr algunas de las cadenas de plantillas de azúcar que se usan en ES5 hoy en día.

Las cadenas de plantilla aportan muchas capacidades importantes a JavaScript. Entre ellas, se incluyen mejores formas de realizar la interpolación de cadenas y expresiones, cadenas de varias líneas y la capacidad de crear tus propios DSL.

Una de las funciones más importantes que aportan son las plantillas etiquetadas, una función fundamental para crear ese tipo de DSL. Reciben las partes de una cadena de plantilla como argumentos y, luego, puedes decidir cómo usar las cadenas y las sustituciones para determinar el resultado final de tu cadena.

Lecturas adicionales