Obtenir des valeurs littérales avec des chaînes de modèle ES6

Addy Osmani
Addy Osmani

Les chaînes en JavaScript ont toujours été limitées, car elles ne disposent pas des fonctionnalités que l'on peut attendre de langages tels que Python ou Ruby. Les chaînes de modèle ES6 (disponibles dans Chrome 41 et versions ultérieures) changent fondamentalement cela. Ils permettent de définir des chaînes avec des langages spécifiques à un domaine (DSL), ce qui améliore les éléments suivants:

  • Interpolation de chaîne
  • Expressions intégrées
  • Chaînes multilignes sans bidouillages
  • Mise en forme des chaînes
  • Etiquetage de chaînes pour une échappement HTML sécurisé, une localisation et plus encore.

Plutôt que d'ajouter une autre fonctionnalité aux chaînes telles que nous les connaissons aujourd'hui, les chaînes de modèle introduisent une manière complètement différente de résoudre ces problèmes.

Syntaxe

Les chaînes de modèle utilisent des guillemets inversés (``) plutôt que les guillemets simples ou doubles que vous utilisez habituellement avec les chaînes standards. Une chaîne de modèle peut donc être écrite comme suit:

var greeting = `Yo World!`;

Jusqu'à présent, les chaînes de modèle ne nous ont pas apporté plus que les chaînes normales. Changeons cela.

Substitution de chaîne

L'un de leurs premiers avantages réels est la substitution de chaînes. La substitution nous permet de prendre n'importe quelle expression JavaScript valide (y compris, par exemple, l'ajout de variables) et, dans une littérale de modèle, le résultat sera affiché dans la même chaîne.

Les chaînes de modèle peuvent contenir des espaces réservés pour la substitution de chaînes à l'aide de la syntaxe ${ }, comme illustré ci-dessous:

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

// => "Yo, Brendan!"

Étant donné que toutes les substitutions de chaînes dans les chaînes de modèle sont des expressions JavaScript, nous pouvons remplacer bien plus que des noms de variables. Par exemple, ci-dessous, nous pouvons utiliser l'interpolation d'expressions pour intégrer des calculs mathématiques lisibles en ligne:

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.

Ils sont également très utiles pour les fonctions dans les expressions:

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

${} fonctionne parfaitement avec n'importe quel type d'expression, y compris les expressions de membres et les appels de méthode:

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 vous avez besoin d'accents graves dans votre chaîne, vous pouvez les échapper à l'aide du caractère barre oblique inverse \ comme suit:

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

Chaînes multilignes

Les chaînes multilignes en JavaScript nécessitent depuis un certain temps des solutions de contournement peu pratiques. Les solutions actuelles exigent que les chaînes existent sur une seule ligne ou soient divisées en chaînes multilignes à l'aide d'un \ (backslash) avant chaque saut de ligne. Exemple :

var greeting = "Yo \
World";

Bien que cela devrait fonctionner correctement dans la plupart des moteurs JavaScript modernes, le comportement lui-même reste un peu bidouillé. Vous pouvez également utiliser la concatenaison de chaînes pour simuler la prise en charge des lignes multiples, mais cela laisse également à désirer:

var greeting = "Yo " +
"World";

Les chaînes de modèle simplifient considérablement les chaînes multilignes. Il vous suffit d'inclure des lignes nouvelles là où elles sont nécessaires. Exemple :

Tout espace vide dans la syntaxe des guillemets arrière est également considéré comme faisant partie de la chaîne.

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

Modèles avec tag

Jusqu'à présent, nous avons vu comment utiliser des chaînes de modèle pour la substitution de chaînes et la création de chaînes multilignes. Ils offrent également une autre fonctionnalité puissante : les modèles tagués. Les modèles tagués transforment une chaîne de modèle en plaçant un nom de fonction avant la chaîne de modèle. Exemple :

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

La sémantique d'une chaîne de modèle taguée est très différente de celle d'une chaîne normale. En substance, il s'agit d'un type spécial d'appel de fonction: le code ci-dessus se "désucre" en

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

Notez que l'argument (n + 1) correspond à la substitution qui a lieu entre la n et la (n + 1) entrées du tableau de chaînes. Cela peut être utile pour toutes sortes de choses, mais l'une des plus simples est l'échappement automatique de toutes les variables interpolées.

Par exemple, vous pouvez écrire une fonction d'échappement HTML de sorte que :

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

renvoie une chaîne avec les variables appropriées remplacées, mais avec tous les caractères non sécurisés en HTML remplacés. Allons-y. Notre fonction d'échappement HTML accepte deux arguments: un nom d'utilisateur et un commentaire. Les deux peuvent contenir des caractères HTML non sécurisés (à savoir : ', ", <, > et &). Par exemple, si le nom d'utilisateur est "Domenic Denicola" et que le commentaire est "& est une balise amusante", nous devrions obtenir :

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

Notre solution de modèle tagué peut donc être écrite comme suit:

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

D'autres utilisations possibles incluent l'échappement automatique, la mise en forme, la localisation et, en général, des substitutions plus complexes:

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

Résumé

Les chaînes de modèle sont disponibles dans Chrome 41 bêta ou version ultérieure, IE Tech Preview, Firefox 35 ou version ultérieure et io.js. En pratique, si vous souhaitez les utiliser en production dès aujourd'hui, ils sont compatibles avec les principaux transpileurs ES6, y compris Traceur et 6to5. Si vous souhaitez les essayer, consultez notre exemple de chaînes de modèle dans le dépôt d'exemples Chrome. Vous pouvez également consulter Équivalents ES6 en ES5, qui explique comment obtenir certains des avantages des chaînes de modèle sucrées à l'aide d'ES5.

Les chaînes de modèle apportent de nombreuses fonctionnalités importantes à JavaScript. Il s'agit notamment de meilleurs moyens d'interpolation des chaînes et des expressions, de chaînes multilignes et de la possibilité de créer vos propres DSL.

L'une des fonctionnalités les plus importantes qu'ils apportent sont les modèles tagués, une fonctionnalité essentielle pour l'écriture de tels DSL. Ils reçoivent les parties d'une chaîne de modèle en tant qu'arguments, et vous pouvez ensuite décider comment utiliser les chaînes et les substitutions pour déterminer la sortie finale de votre chaîne.

Documentation complémentaire