Cómo obtener literales con strings de plantilla de ES6

Addy Osmani
Addy Osmani

Históricamente, las strings en JavaScript han sido limitadas y carecen de las capacidades que uno esperaría que provengan de lenguajes como Python o Ruby. Las cadenas de plantillas de ES6 (disponibles en Chrome 41 y versiones posteriores) cambian esto fundamentalmente. Introducen una forma de definir cadenas con lenguajes específicos de dominio (DSL), lo que mejora lo siguiente:

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

En lugar de incluir otra característica en Strings como las conocemos hoy, las Strings de plantilla presentan una manera completamente diferente de resolver estos problemas.

Sintaxis

Las strings de plantilla usan comillas simples (``) en lugar de las comillas simples o dobles a las que estamos acostumbrados con las strings regulares. Por lo tanto, una string de plantilla se podría escribir de la siguiente manera:

var greeting = `Yo World!`;

Hasta ahora, las cadenas de plantilla no nos proporcionaron 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 válida de JavaScript (por ejemplo, la adición de variables) y, dentro de un literal de plantilla, el resultado se mostrará como parte de la misma cadena.

Las strings de plantilla pueden contener marcadores de posición para la sustitución de strings 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 los nombres de variables. Por ejemplo, a continuación podemos usar la interpolación de expresiones para incorporar algunas operaciones matemáticas 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 funciones dentro de expresiones:

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

${} 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 string, se puede escapar con el carácter de barra inversa \ de la siguiente manera:

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

Strings de varias líneas

Las cadenas de varias líneas en JavaScript requirieron soluciones alternativas ingeniosas durante un tiempo. Las soluciones actuales para ellos requieren que las strings existan en una sola línea o se dividan en strings de varias líneas mediante una \ (barra inversa) antes de cada línea nueva. 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í mismo sigue siendo un truco. También se puede usar la concatenación de cadenas para falsificar la compatibilidad con varias líneas, pero esto también deja algo que desear:

var greeting = "Yo " +
"World";

Las strings de plantilla simplifican de manera significativa las strings de varias líneas. Simplemente incluye líneas nuevas donde sea necesario y bum. Por ejemplo:

Cualquier espacio en blanco dentro de la sintaxis de acento grave también se considerará parte de la string.

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

Plantillas etiquetadas

Hasta ahora, hemos visto el uso de strings de plantilla para la sustitución de cadenas y la creación de cadenas de varias líneas. Otra función poderosa que incluyen son las plantillas etiquetadas. Las plantillas etiquetadas transforman una string de plantilla colocando un nombre de función antes de la string de plantilla. Por ejemplo:

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

La semántica de una string de plantilla etiquetada es muy diferente de la de una string normal. En esencia, son un tipo especial de llamada a función: la "expansión de sintaxis" antes 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 las entradas enésimas y (n + 1) del conjunto 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 HTML tal como...

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

muestra una cadena con las variables correspondientes sustituidas, pero con todos los caracteres no seguros para HTML reemplazados. Manos a la obra. Nuestra función de escape HTML tomará dos argumentos: un nombre de usuario y un comentario. Ambos pueden contener caracteres no seguros de HTML (es decir, ', ", <, > y &). Por ejemplo, si el nombre de usuario es "Domenic Denicola" y el comentario es "& is a fun tag", deberíamos obtener el siguiente resultado:

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

Así, nuestra solución de plantilla etiquetada 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, e io.js. En términos prácticos, si deseas usarlos en la producción actualmente, son compatibles con los principales transpiladores ES6, incluidos Traceur y 6to5. Si quieres probarlas, consulta nuestro ejemplo de cadenas de plantillas en el repositorio de muestras de Chrome. Es posible que también te interesen los equivalentes de ES6 en ES5, que demuestran cómo lograr algunas de las cadenas de plantilla que usan ES5 en la actualidad.

Las strings de plantilla aportan muchas capacidades importantes a JavaScript. Estos incluyen mejores formas de interpolar cadenas y expresiones, cadenas de varias líneas y la capacidad de crear tus propias DSL.

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

Material de lectura adicional