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

Addy Osmani
Addy Osmani

Les chaînes en JavaScript ont été historiquement limitées, et elles ne disposent pas des fonctionnalités que l'on peut attendre de langages comme Python ou Ruby. Les chaînes de modèle ES6 (disponibles dans Chrome 41 et versions ultérieures) changent radicalement la donne. Ils permettent de définir des chaînes avec des langages spécifiques au domaine (DSL), ce qui offre de meilleurs résultats:

  • Interpolation de chaîne
  • Expressions intégrées
  • Chaînes multilignes sans hack
  • Mise en forme des chaînes
  • Ajout de tags de chaîne pour l'échappement, la localisation et la sécurité du code HTML, entre autres.

Plutôt que d'accumuler une autre fonctionnalité dans les 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 obliques (``) plutôt que les guillemets simples ou doubles auxquels nous sommes habitués avec les chaînes régulières. Un modèle de chaîne peut ainsi être écrit comme suit:

var greeting = `Yo World!`;

Jusqu'à présent, les chaînes de modèle ne nous ont fourni rien de plus que les chaînes normales. Il est temps de changer cela.

Remplacement de chaîne

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

Les chaînes de modèle peuvent contenir des espaces réservés pour la substitution de chaîne à l'aide de la syntaxe ${ }, comme indiqué 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 substituer beaucoup plus que les noms de variables. Par exemple, ci-dessous, nous pouvons utiliser l'interpolation d'expression pour intégrer des fonctions mathématiques lisibles:

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 des expressions:

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

${} fonctionne avec tout type d'expression, y compris les expressions de membre 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'intercalaires dans votre chaîne, vous pouvez l'échapper à l'aide de la barre oblique inverse \, comme suit:

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

Chaînes multilignes

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

var greeting = "Yo \
World";

Même si cela devrait fonctionner correctement dans la plupart des moteurs JavaScript récents, le comportement en lui-même reste un peu difficile. Vous pouvez également utiliser la concaténation de chaînes pour simuler une prise en charge multiligne, 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 sauts de ligne là où ils sont nécessaires et le BOOM. Exemple :

Tout espace blanc à l'intérieur de la syntaxe d'accent grave sera é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 étudié l'utilisation de chaînes de modèle pour la substitution de chaînes et pour la création de chaînes multilignes. Ils apportent é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 du 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 d'appel de fonction spécial: les "désucrages" ci-dessus dans

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

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

Par exemple, vous pouvez écrire une fonction d'échappement HTML telle que...

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

renvoie une chaîne dans laquelle les variables appropriées sont remplacées, mais tous les caractères non compatibles HTML sont remplacés. C'est parti. La fonction d'échappement HTML prend deux arguments: un nom d'utilisateur et un commentaire. Les deux peuvent contenir des caractères HTML non fiables (à savoir ', ", <, > et &). Par exemple, si le nom d'utilisateur est "Domenic Denicola" et que le commentaire est "& est une balise amusante", vous devriez obtenir le résultat suivant:

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

Notre modèle de solution taguée peut donc être rédigé 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"

Parmi les autres utilisations possibles, citons 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 (version bêta ou ultérieure), en version Preview technique d'IE, dans Firefox 35 ou version ultérieure et dans io.js. En pratique, si vous souhaitez les utiliser en production aujourd'hui, sachez qu'ils sont compatibles avec les principaux transpilateurs ES6, dont 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 serez peut-être également intéressé par les équivalents ES6 dans ES5, qui montrent comment obtenir certaines chaînes de modèle qui permettent d'utiliser ES5 à l'heure actuelle.

Les chaînes de modèle apportent de nombreuses fonctionnalités importantes à JavaScript. Vous bénéficiez par exemple de meilleurs moyens d'interpoler des chaînes et des expressions, de chaînes multilignes et de la possibilité de créer vos propres DSL.

Les modèles tagués constituent l'une des fonctionnalités les plus importantes qu'ils apportent. Il s'agit d'une fonctionnalité essentielle pour la création 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 de la façon d'utiliser les chaînes et les substitutions pour déterminer la sortie finale de votre chaîne.

Documentation complémentaire