Las nuevas cadenas de texto de JavaScript 6

10 de febrero de 2015

Las cadenas de texto de JavaScript han sido desde siempre muy limitadas, sobre todo comparadas con las cadenas de lenguajes como PHP, Python o Ruby. Las Template Strings del estándar EcmaScript 6 (que están disponibles a partir de Google Chrome 41) eliminan todas estas limitaciones y permiten incluso definir DSLs (domain-specific languages) en tus aplicaciones JavaScript.

Las principales funcionalidades que aportan las Template Strings son:

  • Interpolación de cadenas.
  • Posibilidad de incluir (y evaluar) expresiones dentro de cadenas.
  • Definición de cadenas de texto en varias líneas sin tener que usar hacks.
  • Formatear cadenas de manera avanzada.
  • Cadenas etiquetadas.

En vez de añadir todas estas funcionalidades en las cadenas que conocemos y usamos a diario en JavaScript, las Template Strings añaden sus funcionalidades de una manera completamente nueva.

Sintaxis

Las Template Strings utilizan las comillas invertidas o backticks para delimitar sus contenidos, en vez de las tradicionales comillas simples o dobles de las cadenas de texto normales. Así que este es un ejemplo de cómo definir una Template String:

// esto es una Template String
var saludo = `¡Hola Mundo!`;

// esto es una cadena normal con comillas simples
var saludo = '¡Hola Mundo!';

// esto es una cadena normal con comillas dobles
var saludo = "¡Hola Mundo!";

Aunque de momento las Template Strings no parecen gran cosa, sigue leyendo para descubrir todas sus funcionalidades.

Interpolación de cadenas

Una de las mejores características de las Template Strings es la interpolación de cadenas. En pocas palabras, la interpolación permite utilizar cualquier expresión válida de JavaScript (como por ejemplo la suma de dos variables) dentro de una cadena y obtener como resultado la cadena completa con la expresión evaluada.

Las partes variables de una Template String se denominan placeholders y utilizan la sintaxis ${ } para diferenciarse del resto de la cadena. Ejemplo:

// Sustitución simple de cadenas
var nombre = "Juan";
console.log(`¡Hola ${nombre}!`);

// resultado => "¡Hola Juan!"

Como dentro de las partes variables de la cadena se puede incluir cualquier expresión válida de JavaScript, en la práctica sirven para mucho más que mostrar el contenido de una variable. En los siguientes ejemplos se muestran cómo interpolar algunas operaciones matemáticas sencillas:

var a = 10;
var b = 10;
console.log(`¡JavaScript se publicó hace ${a+b} años!`);

// resultado => ¡JavaScript se publicó hace 20 años!

console.log(`Existen ${2 * (a + b)} frameworks JavaScript y no ${10 * (a + b)}.`);
// resultado => Existen 40 frameworks JavaScript y no 2000.

Dentro de un valor interpolado también se puede utilizar cualquier función:

function fn() { return "Este es el resultado de la función"; }
console.log(`Hola "${fn()}" Mundo`);
// resultado => Hola "Este es el resultado de la función" Mundo

La sintaxis ${} también funciona con expresiones que invocan métodos y acceden a propiedades:

var usuario = { nombre: 'Juan Perez' };
console.log(`Estás conectado como ${usuario.nombre.toUpperCase()}.`);

// resultado => "Estás conectado como JUAN PEREZ.";

var divisa = 'Euro';
console.log(`Los precios se indican en ${divisa}. Utiliza nuestro conversor para convertir ${divisa} en tu moneda local.`);

// resultado => Los precios se indican en Euro. Utiliza nuestro conversor para convertir Euro en tu moneda local.

Si la Template String contiene en su interior las mismas comillas que se usan para delimitar sus contenidos, escápalas usando la habitual barra invertida \ delante de la comilla:

var saludo = `¡\`Hola\` Mundo!`;

Cadenas en varias líneas

Definir una cadena de texto en varias líneas con JavaScript es sencillo, pero requiere de algunos hacks que ensucian el código de la aplicación. La solución más utilizada actualmente consiste en añadir una barra invertida antes de cada salto de línea. Ejemplo:

var saludo = "Hola \
Mundo";

Aunque este código funciona perfectamente en cualquier engine moderno de JavaScript, no deja de ser un hack. Otra forma de definir cadenas en varias líneas consiste en utilizar la concatenación de cadenas, aunque el resultado no es mucho mejor que el anterior:

var saludo = "Hola " +
"Mundo";

Las Template Strings simplifican drásticamente la definición de cadenas en varias líneas. De hecho, es tan sencillo que no hay que hacer absolutamente nada: pulsa <Enter> cada vez que quieras incluir un salto de línea en la cadena y ya está. Ejemplo:

var cadena = `Línea número 1 de la cadena
Línea número 2 de la cadena`;

Este código funciona bien porque cualquier espacio en blanco (incluyendo los saltos de línea) que se incluyen dentro de las comillas invertidas se considera que es parte de la cadena.

Cadenas etiquetadas

Otra de las funcionalidades avanzadas de las Template Strings son las "tagged strings" o cadenas etiquetadas. Las etiquetas permiten transformar los contenidos de una cadena aplicándoles una función. El nombre de la etiqueta se indica justo delante de la cadena y su nombre coincide con el de la función que se ejecutará para transformar los contenidos de la cadena:

fn`¡Hola ${nombre}! Tu última conexión fue el ${fechaUltimaConexion}.`

En este ejemplo, fn es el nombre de la etiqueta y por tanto, también es el nombre de la función que se ejecuta. El siguiente código muestra cómo se transforman realmente los contenidos de la Template String en los argumentos de la función:

fn(["¡Hola ", "! Tu última conexión fue el  ", "."], nombre, fechaUltimaConexion);

A continuación, se muestra un ejemplo práctico del uso de las cadenas etiquetadas. Imagina que en tu aplicación necesitas escapar todo el contenido HTML que puedan contener las cadenas de texto. Gracias a las cadenas etiquetadas, podrías definir una etiqueta llamada html y utilizarla de esta manera:

html`<b>${nombreUsuario} dice</b>: "${comentario}"`

Lo que queremos es que el contenido de las variables nombreUsuario y comentario se escape automáticamente para que no "rompan" el contenido HTML de la página. La función a desarrollar tendrá dos argumentos llamados nombreUsuario y comentario. Los dos argumentos pueden contener caracteres problemáticos desde el punto de vista HTML (<, >, &, etc.)

La función html() que utilizaremos como etiqueta podría tener este contenido:

function html(parametros) {
    var resultado = parametros[0];
    var substituciones = [].slice.call(arguments, 1); 
    for (var i = 0; i < substituciones.length; ++i) {
        resultado += escape(substituciones[i]) + parametros[i + 1];
    }

    return resultado;
}

function escape(s) {
    return s.replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/'/g, "&#39;")
            .replace(/"/g, "&quot;");
}

var nombreUsuario = "Juan Perez";
var comentario = "aprendiendo <em>JavaScript</em> & <b>ES6</b>";

console.log(html`<b>${nombreUsuario} dice</b>: "${comentario}"`);
// resultado => <b>Juan Perez dice</b>: "aprendiendo &lt;em&gt;JavaScript&lt;/em&gt; &amp; &lt;b&gt;ES6&lt;/b&gt;"

Las posibilidades de las cadenas etiquetadas son prácticamente ilimitadas. Puedes utilizarlas para escapar información, formatear cadenas, traducirlas y cualquier otra sustitución avanzada que puedas imaginar:

// Escapar información
qsa`.${className}`;
safehtml`<a href="${url}?q=${query}" onclick="alert('${mensaje}')" style="color: ${color}">${mensaje}</a>`;

// Formatear valores en función del idioma (:numero y :divisa transforman el contenido)
l10n`Hola ${nombre}; eres nuestro visitante ${visitante}:numero. El saldo de tu cuenta es ${dinero}:divisa.`

// Embeber HTML/XML
jsx`<a href="${url}">${texto}</a>` // se transforma en React.DOM.a({ href: url }, texto)

// DSL propio para ejecutar comandos
var proceso = sh`ps ax | grep ${pid}`;

Resumen y referencias

Las Template Strings se pueden utilizar en Google Chrome 41, la Tech Preview de Internet Explorer, Firefox 35 y el framework io.js. La mayor parte de transpilers de ES6 también las soportan, como por ejemplo Tracuer.

Puedes encontrar más ejemplos sobre Template Strings en el repositorio de ejemplos de código de Google Chrome. Consulta también este artículo para descubrir el código equivalente en ES5 para conseguir una funcionalidad similar a la ofrecida por ES6.

Las Template Strings añaden funcionalidades muy interesantes a las cadenas de texto tradicionales de JavaScript. Entre otras, permiten interpolar expresiones, definir cadenas en varias líneas y crear tu propio lenguaje DSL gracias a las cadenas etiquetadas.

Esta última es la funcionalidad más importante, ya que gracias a las etiquetas, las aplicaciones pueden definir sus propios lenguajes DSL. En la práctica, las etiquetas reciben como argumentos las diferentes partes de una cadena y los procesan para obtener como resultado una nueva cadena.

Referencias interesantes

Sobre el autor

Este artículo fue publicado originalmente por Addy Osmani y ha sido traducido con permiso por Javier Eguiluz.