Letterlijk met ES6-templates

Tekenreeksen in JavaScript waren in het verleden beperkt en hadden niet de mogelijkheden die je misschien verwacht van talen als Python of Ruby. ES6 Template Strings (beschikbaar in Chrome 41 en hoger) veranderen dat fundamenteel. Ze bieden een manier om tekenreeksen te definiëren met domeinspecifieke talen (DSL's), wat leidt tot betere:

  • Tekenreeksinterpolatie
  • Ingesloten expressies
  • Tekenreeksen met meerdere regels zonder hacks
  • Tekenreeksopmaak
  • String-tagging voor veilige HTML-escaping, lokalisatie en meer.

In plaats van nog een functie in Strings te proppen, introduceren Template Strings een heel andere manier om deze problemen op te lossen.

Syntaxis

Template-tekenreeksen gebruiken backticks (``) in plaats van de enkele of dubbele aanhalingstekens die we gewend zijn bij gewone tekenreeksen. Een templatestring kan dus zo worden geschreven:

var greeting = `Yo World!`;

Tot nu toe hebben Template Strings ons niets meer opgeleverd dan normale tekenreeksen. Laten we dat veranderen.

Tekenreeksvervanging

Een van de eerste echte voordelen is tekenreeksvervanging. Met vervanging kunnen we elke geldige JavaScript-expressie (bijvoorbeeld de optelling van variabelen) gebruiken en wordt het resultaat in een sjabloonletterlijk uitgevoerd als onderdeel van dezelfde tekenreeks.

Template-tekenreeksen kunnen tijdelijke aanduidingen voor tekenreeksvervanging bevatten met de syntaxis ${ }, zoals hieronder getoond:

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

// => "Yo, Brendan!"

Omdat alle tekenreeksvervangingen in templates JavaScript-expressies zijn, kunnen we veel meer vervangen dan alleen variabelenamen. Hieronder gebruiken we bijvoorbeeld expressie-interpolatie om leesbare inline wiskunde in te sluiten:

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.

Ze zijn ook erg handig voor functies in expressies:

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

De ${} werkt prima met elk type expressie, waaronder lidexpressies en methodeaanroepen:

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

Als u backticks in uw tekenreeks nodig heeft, kunt u deze escapen met de backslash \, zoals hieronder:

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

Tekenreeksen met meerdere regels

Voor tekenreeksen met meerdere regels in JavaScript zijn al een tijdje omslachtige tijdelijke oplossingen nodig. Voor de huidige oplossingen moeten tekenreeksen op één regel staan of worden gesplitst in tekenreeksen van meerdere regels met een \ (backslash) vóór elke nieuwe regel. Voorbeeld:

var greeting = "Yo \
World";

Hoewel dit in de meeste moderne JavaScript-engines prima werkt, is het gedrag zelf nog steeds een beetje een hack. Je kunt ook tekenreeksen samenvoegen om ondersteuning voor meerdere regels na te bootsen, maar ook dit laat te wensen over:

var greeting = "Yo " +
"World";

Template-tekenreeksen maken tekenreeksen met meerdere regels veel eenvoudiger. Voeg gewoon nieuwe regels toe waar nodig en BOEM. Hier volgt een voorbeeld:

Witruimte binnen de backtick-syntaxis wordt ook beschouwd als onderdeel van de tekenreeks.

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

Getagde templates

Tot nu toe hebben we gekeken naar het gebruik van template-tekenreeksen voor tekenreeksvervanging en voor het maken van tekenreeksen met meerdere regels. Een andere krachtige functie is dat ze getagde templates bieden. Getagde templates transformeren een templatetekenreeks door een functienaam vóór de templatetekenreeks te plaatsen. Voorbeeld:

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

De semantiek van een getagde templatestring verschilt sterk van die van een normale templatestring. In feite zijn ze een speciaal type functieaanroep: het bovenstaande wordt 'ontzoet' tot

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

Let op hoe het (n + 1)e argument overeenkomt met de vervanging die plaatsvindt tussen de n-de en (n + 1)e invoer in de tekenreeksarray. Dit kan voor allerlei zaken handig zijn, maar een van de meest eenvoudige is het automatisch escapen van geïnterpoleerde variabelen.

Je kunt bijvoorbeeld een functie schrijven om HTML te escapen, zodat...

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

retourneert een tekenreeks waarin de juiste variabelen zijn ingevoegd, maar waarin alle HTML-onveilige tekens zijn vervangen. Laten we dat doen. Onze functie voor HTML-escaping neemt 2 argumenten: een gebruikersnaam en een reactie. Beide kunnen HTML-onveilige tekens bevatten (namelijk ', ", <, > en &). Als de gebruikersnaam bijvoorbeeld Domenic Denicola is en de reactie "& is een leuke tag", moeten we het volgende uitvoeren:

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

Onze oplossing voor getagde templates kan dus als volgt worden geschreven:

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

Andere mogelijke toepassingen zijn automatische escapen, opmaak, lokalisatie en in het algemeen complexere vervangingen:

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

Overzicht

Template Strings zijn beschikbaar in Chrome 41 bèta of hoger, IE Tech Preview, Firefox 35 of hoger en io.js. Als je ze vandaag in productie wilt gebruiken, worden ze ondersteund in belangrijke ES6-transpilers, waaronder Traceur en 6to5. Ga naar onze voorbeeld van template-tekenreeksen in de Chrome-voorbeeldrepository als je ze wilt uitproberen. Je bent misschien ook geïnteresseerd in ES6-equivalenten in ES5. Hierin staat hoe je een aantal van de voordelen van templates in ES5 kunt gebruiken.

Template-tekenreeksen bieden veel belangrijke mogelijkheden voor JavaScript. Dit zijn onder andere betere manieren om tekenreeks- en expressie-interpolatie uit te voeren, tekenreeksen van meerdere regels en de mogelijkheid om je eigen DSL's te maken.

Een van de belangrijkste functies die ze bieden, zijn getagde sjablonen. Dit is een essentiële functie om dergelijke DSL's te maken. Ze krijgen de delen van een template-tekenreeks als argumenten en je kunt dan bepalen hoe je de tekenreeksen en vervangingen gebruikt om de uiteindelijke uitvoer van je tekenreeks te bepalen.

Aanvullende informatie