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

Les chaînes JavaScript ont toujours été limitées et ne disposent pas des fonctionnalités auxquelles on pourrait s'attendre avec des langages comme Python ou Ruby. Les chaînes de modèles ES6 (disponibles dans Chrome 41 et versions ultérieures) changent fondamentalement la donne. Ils permettent de définir des chaînes avec des langages spécifiques à un domaine (DSL), ce qui améliore :

  • Interpolation de chaînes
  • Expressions intégrées
  • Chaînes multilignes sans astuces
  • Mise en forme des chaînes
  • Ajout de tags aux chaînes pour l'échappement HTML sécurisé, la localisation et plus encore.

Au lieu d'ajouter une fonctionnalité supplémentaire 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 accents graves (``) plutôt que les guillemets simples ou doubles que nous utilisons habituellement avec les chaînes classiques. Une chaîne de modèle peut donc être écrite comme suit :

var greeting = `Yo World!`;

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

Substitution de chaînes

L'un de leurs premiers avantages concrets est la substitution de chaînes. La substitution nous permet de prendre n'importe quelle expression JavaScript valide (y compris, par exemple, l'addition de variables) et, dans un littéral 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!"

Comme toutes les substitutions de chaînes dans les chaînes de modèle sont des expressions JavaScript, nous pouvons substituer beaucoup plus que des noms de variables. Par exemple, ci-dessous, nous pouvons utiliser l'interpolation d'expression pour intégrer des formules mathématiques en ligne 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.

Elles sont également très utiles pour les fonctions à l'intérieur des expressions :

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

${} fonctionne correctement avec n'importe quel 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'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 longtemps des solutions de contournement complexes. Les solutions actuelles exigent 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";

Bien que cela devrait fonctionner correctement dans la plupart des moteurs JavaScript modernes, le comportement lui-même reste un peu une astuce. Il est également possible d'utiliser la concaténation de chaînes pour simuler la 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 tour est joué. Exemple :

Tout espace blanc à l'intérieur de la syntaxe des accents graves sera également considéré comme faisant partie de la chaîne.

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

Modèles tagués

Jusqu'à présent, nous avons vu comment utiliser les chaînes de modèle pour la substitution de chaînes et la création de chaînes multilignes. Les modèles tagués sont une autre fonctionnalité puissante qu'ils proposent. 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 : l'exemple ci-dessus se "désucre" en

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

Notez que le (n+1)e argument correspond à la substitution qui a lieu entre la ne et la (n+1)e entrée 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 substituées, mais avec tous les caractères non sécurisés pour HTML remplacés. C'est ce que nous allons faire à présent. Notre fonction d'échappement HTML prendra 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 le commentaire est "& est un tag amusant", nous devons afficher :

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

Notre solution de modèle tagué pourrait donc s'écrire 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èles sont disponibles dans Chrome 41 bêta et versions ultérieures, IE Tech Preview, Firefox 35 et versions ultérieures, et io.js. En pratique, si vous souhaitez les utiliser en production aujourd'hui, ils sont compatibles avec les principaux transpilers 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 dans ES5, qui montre comment obtenir certains des avantages des chaînes de modèles à l'aide d'ES5 aujourd'hui.

Les chaînes de modèle apportent de nombreuses fonctionnalités importantes à JavaScript. Cela inclut de meilleures façons d'effectuer l'interpolation de chaînes et d'expressions, les chaînes multilignes et la possibilité de créer vos propres DSL.

L'une des fonctionnalités les plus importantes qu'ils apportent est celle des modèles tagués, qui est essentielle pour la création de tels DSL. Ils reçoivent les parties d'une chaîne de modèle en tant qu'arguments. Vous pouvez ensuite décider comment utiliser les chaînes et les substitutions pour déterminer le résultat final de votre chaîne.

Documentation complémentaire