Uso letterale con le stringhe dei modelli ES6

Addy Osmani
Addy Osmani

Le stringhe in JavaScript sono storicamente limitate, perché mancano le funzionalità che ci si potrebbe aspettare da linguaggi come Python o Ruby. Le stringhe modello ES6 (disponibili in Chrome 41 e versioni successive) hanno cambiato radicalmente questa situazione. Introducono un modo per definire le stringhe con linguaggi specifici del dominio (DSL), migliorando:

  • Interpolazione di stringhe
  • Espressioni incorporate
  • Stringhe multiriga senza hack
  • Formattazione delle stringhe
  • Tagging delle stringhe per l'escape in sicurezza del codice HTML, la localizzazione e altro ancora.

Anziché includere un'altra funzionalità nelle stringhe come le conosciamo oggi, le stringhe di modello introducono un modo completamente diverso di risolvere questi problemi.

Sintassi

Le stringhe del modello usano i tic (``) invece delle virgolette singole o doppie che siamo abituati a utilizzare nelle stringhe normali. Una stringa modello potrebbe quindi essere scritta come segue:

var greeting = `Yo World!`;

Finora, le stringhe di modello non hanno fornito niente di più delle stringhe normali. Cambiamolo.

Sostituzione stringhe

Uno dei primi vantaggi concreti è la sostituzione delle stringhe. La sostituzione ci consente di prendere qualsiasi espressione JavaScript valida (compresa, ad esempio, l'aggiunta di variabili) e all'interno di un modello letterale il risultato verrà restituito come parte della stessa stringa.

Le stringhe del modello possono contenere segnaposto per la sostituzione delle stringhe utilizzando la sintassi ${ }, come mostrato di seguito:

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

// => "Yo, Brendan!"

Dal momento che tutte le sostituzioni di stringhe nelle stringhe modello sono espressioni JavaScript, possiamo sostituire molte altre sostituzioni dei nomi di variabili. Ad esempio, di seguito possiamo utilizzare l'interpolazione delle espressioni per incorporare alcuni calcoli matematici in linea leggibili:

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.

Sono molto utili anche per le funzioni all'interno delle espressioni:

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

${} funziona bene con qualsiasi tipo di espressione, incluse le espressioni membro e le chiamate ai metodi:

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

Se sono necessari apici indietro all'interno della stringa, puoi eseguire l'escape utilizzando il carattere della barra rovesciata \ nel seguente modo:

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

Stringhe multilinea

Per un certo periodo di tempo, le stringhe multilinea in JavaScript hanno richiesto soluzioni alternative ingannevoli. Le soluzioni attuali richiedono che le stringhe esistano su una singola riga o che siano suddivise in stringhe su più righe utilizzando una \ (barra rovesciata) prima di ogni nuova riga. Ad esempio:

var greeting = "Yo \
World";

Anche se questo metodo dovrebbe funzionare bene nella maggior parte dei motori JavaScript moderni, il comportamento in sé è ancora un po' una compromissione. È anche possibile utilizzare la concatenazione di stringhe per falsificare il supporto multilinea, ma ciò lascia ugualmente qualcosa a desiderare:

var greeting = "Yo " +
"World";

Le stringhe di modello semplificano notevolmente le stringhe su più righe. Basta inserire i ritorni a capo quando servono e BOOM. Esempio:

Anche eventuali spazi vuoti all'interno della sintassi dell'accento grave verranno considerati parte della stringa.

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

Modelli con tag

Finora abbiamo visto l'utilizzo delle stringhe modello per la sostituzione delle stringhe e per la creazione di stringhe multilinea. Un'altra potente funzionalità che offrono sono i modelli con tag. I modelli con tag trasformano una stringa modello inserendo il nome di una funzione prima della stringa del modello. Ad esempio:

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

La semantica di una stringa modello con tag è molto diversa da quella di una stringa normale. In sostanza, si tratta di un tipo speciale di chiamata di funzione: i precedenti "desugari" in

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

Si noti come l'argomento (n + 1) corrisponde alla sostituzione che avviene tra le voci n-esima e (n + 1)esima nell'array di stringhe. Questo può essere utile per qualsiasi cosa, ma una delle più semplici è l'escape automatico di qualsiasi variabile interpolata.

Ad esempio, puoi scrivere una funzione di escape HTML in modo che...

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

restituisce una stringa con le variabili appropriate sostituite, ma con tutti i caratteri non sicuri per HTML sostituiti. Procediamo. La nostra funzione di escape HTML accetta due argomenti: un nome utente e un commento. Entrambi possono contenere caratteri HTML non sicuri (vale a dire ', ", <, > e &). Ad esempio, se il nome utente è "Domenic Denicola" e il commento è "& is a fun tag", dovremmo produrre:

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

La nostra soluzione di modello con tag potrebbe quindi essere scritta come segue:

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

Altri possibili utilizzi includono l'escape automatico, la formattazione, la localizzazione e in generale le sostituzioni più complesse:

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

Riepilogo

Le stringhe modello sono in Chrome 41 beta e versioni successive, IE Tech Preview, Firefox 35 e versioni successive e io.js. In pratica, se vuoi utilizzarli in produzione oggi, sono supportati dai principali transpiler ES6, tra cui Traceur e 6to5. Se vuoi provarle, consulta il nostro esempio di stringhe modello nel repository degli esempi di Chrome. Potresti anche essere interessato all'articolo ES6 Equivalents in ES5, che illustra come ottenere alcune delle stringhe modello di zucchero portate oggi utilizzando ES5.

Le stringhe di modello integrano molte importanti funzionalità in JavaScript. Questi includono modi migliori per eseguire l'interpolazione di stringhe ed espressioni, stringhe multilinea e la possibilità di creare le tue DSL.

Una delle funzionalità più significative che apporta sono i modelli con tag, una funzionalità fondamentale per la creazione di tali DSL. Essi ricevono le parti di una stringa modello come argomenti e tu puoi quindi decidere come utilizzare le stringhe e le sostituzioni per determinare l'output finale della stringa.

Per approfondire