\n)String.rawES6 hat zwei neue Arten von Literalen: *Template-Literale* und *Tagged Template-Literale*. Diese beiden Literale haben ähnliche Namen und sehen ähnlich aus, sind aber recht unterschiedlich. Es ist daher wichtig, zu unterscheiden
*Template-Literale* sind String-Literale, die sich über mehrere Zeilen erstrecken und interpolierte Ausdrücke (eingefügt über ${···}) enthalten können.
const firstName = 'Jane';
console.log(`Hello ${firstName}!
How are you
today?`);
// Output:
// Hello Jane!
// How are you
// today?
*Tagged Template-Literale* (kurz: *Tagged Templates*) werden erstellt, indem eine Funktion vor einem Template-Literal genannt wird.
> String.raw`A \tagged\ template`
'A \\tagged\\ template'
Tagged Templates sind Funktionsaufrufe. Im vorherigen Beispiel wird die Methode String.raw aufgerufen, um das Ergebnis des Tagged Templates zu erzeugen.
Literale sind syntaktische Konstrukte, die Werte erzeugen. Beispiele hierfür sind String-Literale (die Strings erzeugen) und reguläre Ausdrucks-Literale (die reguläre Ausdrucksobjekte erzeugen). ECMAScript 6 hat zwei neue Literale
Es ist wichtig zu bedenken, dass die Namen von Template-Literale und Tagged Templates leicht irreführend sind. Sie haben nichts mit *Templates* zu tun, wie sie oft in der Webentwicklung verwendet werden: Textdateien mit Platzhaltern, die über (z. B.) JSON-Daten gefüllt werden können.
Ein Template-Literal ist eine neue Art von String-Literal, die sich über mehrere Zeilen erstrecken und Ausdrücke *interpolieren* (deren Ergebnisse einschließen) kann. Zum Beispiel
const firstName = 'Jane';
console.log(`Hello ${firstName}!
How are you
today?`);
// Output:
// Hello Jane!
// How are you
// today?
Das Literal selbst wird durch Backticks (`) begrenzt, die interpolierten Ausdrücke innerhalb des Literals werden durch ${ und } begrenzt. Template-Literale erzeugen immer Strings.
Der Backslash wird zum Escaping innerhalb von Template-Literale verwendet.
Er ermöglicht es Ihnen, Backticks und ${ in Template-Literale einzufügen
> `\``
'`'
> `$` // OK
'$'
> `${`
SyntaxError
> `\${`
'${'
> `\${}`
'${}'
Ansonsten funktioniert der Backslash wie in String-Literale
> `\\`
'\\'
> `\n`
'\n'
> `\u{58}`
'X'
\n) Gängige Methoden zur Beendigung von Zeilen sind
\n, U+000A): verwendet von Unix (einschließlich des aktuellen macOS)\r, U+000D): verwendet vom alten Mac OS.\r\n): verwendet von Windows.Alle diese Zeilenumbrüche werden in Template-Literale auf LF normalisiert. Das heißt, der folgende Code gibt auf allen Plattformen true aus
const str = `BEFORE
AFTER`;
console.log(str === 'BEFORE\nAFTER'); // true
Das Folgende ist ein *Tagged Template-Literal* (kurz: *Tagged Template*)
tagFunction`Hello ${firstName} ${lastName}!`
Das Setzen eines Template-Literals nach einem Ausdruck löst einen Funktionsaufruf aus, ähnlich wie eine Parameterliste (kommagetrennte Werte in Klammern) einen Funktionsaufruf auslöst. Der vorherige Code ist äquivalent zum folgenden Funktionsaufruf (in Wirklichkeit ist der erste Parameter mehr als nur ein Array, aber das wird später erklärt).
tagFunction(['Hello ', ' ', '!'], firstName, lastName)
Somit ist der Name vor dem Inhalt in Backticks der Name einer Funktion, die aufgerufen werden soll, die *Tag-Funktion*. Die Tag-Funktion erhält zwei verschiedene Arten von Daten
'Hallo '.firstName (begrenzt durch ${}). Eine Substitution kann jeder Ausdruck sein.Template-Strings sind statisch bekannt (zur Kompilierzeit), Substitutionen sind nur zur Laufzeit bekannt. Die Tag-Funktion kann mit ihren Parametern beliebig verfahren: Sie kann die Template-Strings vollständig ignorieren, Werte beliebigen Typs zurückgeben usw.
Zusätzlich erhalten Tag-Funktionen zwei Versionen jedes Template-Strings
`\n` wird zu '\\n', ein String der Länge 2)`\n` wird zu einem String mit nur einem Zeilenumbruch darin).Das ermöglicht String.raw (das später erklärt wird), seine Arbeit zu tun.
> String.raw`\n` === '\\n'
true
Tagged Template-Literale ermöglichen es Ihnen, benutzerdefinierte eingebettete Subsprachen (die manchmal als *domänenspezifische Sprachen* bezeichnet werden) mit wenig Aufwand zu implementieren, da JavaScript viel vom Parsen für Sie übernimmt. Sie müssen nur eine Funktion schreiben, die die Ergebnisse erhält.
Schauen wir uns Beispiele an. Einige von ihnen sind inspiriert von dem ursprünglichen Vorschlag für Template-Literale, der sie unter ihrem alten Namen *Quasi-Literale* erwähnt.
ES6 enthält die Tag-Funktion String.raw für *rohe Strings*, bei denen Backslashes keine spezielle Bedeutung haben.
const str = String.raw`This is a text
with multiple lines.
Escapes are not interpreted,
\n is not a newline.`;
Dies ist nützlich, wann immer Sie Strings erstellen müssen, die Backslashes enthalten. Zum Beispiel
function createNumberRegExp(english) {
const PERIOD = english ? String.raw`\.` : ','; // (A)
return new RegExp(`[0-9]+(${PERIOD}[0-9]+)?`);
}
In Zeile A ermöglicht uns String.raw, den Backslash so zu schreiben, wie wir es in einem regulären Ausdrucks-Literal tun würden. Bei normalen String-Literalen müssen wir doppelt escapen: Zuerst müssen wir den Punkt für den regulären Ausdruck escapen. Zweitens müssen wir den Backslash für das String-Literal escapen.
const proc = sh`ps ax | grep ${pid}`;
(Quelle: David Herman)
const buffer = bytes`455336465457210a`;
(Quelle: David Herman)
POST`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}
`
(myOnReadyStateChangeHandler);
(Quelle: Luke Hoban)
Steven Levithan hat ein Beispiel dafür gegeben, wie Tagged Template-Literale für seine Bibliothek für reguläre Ausdrücke XRegExp verwendet werden könnten.
Ohne Tagged Templates schreiben Sie Code wie den folgenden
var parts = '/2015/10/Page.html'.match(XRegExp(
'^ # match at start of string only \n' +
'/ (?<year> [^/]+ ) # capture top dir name as year \n' +
'/ (?<month> [^/]+ ) # capture subdir name as month \n' +
'/ (?<title> [^/]+ ) # capture base name as title \n' +
'\\.html? $ # .htm or .html file ext at end of path ', 'x'
));
console.log(parts.year); // 2015
Wir sehen, dass XRegExp uns benannte Gruppen (year, month, title) und das x-Flag gibt. Mit diesem Flag wird der meiste Leerraum ignoriert und Kommentare können eingefügt werden.
Es gibt zwei Gründe, warum String-Literale hier nicht gut funktionieren. Erstens müssen wir jeden Backslash eines regulären Ausdrucks zweimal tippen, um ihn für das String-Literal zu escapen. Zweitens ist es umständlich, mehrere Zeilen einzugeben.
Anstatt Strings zu verketten, können Sie auch einen String-Literal in der nächsten Zeile fortsetzen, wenn Sie die aktuelle Zeile mit einem Backslash beenden. Aber das ist immer noch mit viel visueller Unordnung verbunden, besonders weil Sie immer noch den expliziten Zeilenumbruch über \n am Ende jeder Zeile benötigen.
var parts = '/2015/10/Page.html'.match(XRegExp(
'^ # match at start of string only \n\
/ (?<year> [^/]+ ) # capture top dir name as year \n\
/ (?<month> [^/]+ ) # capture subdir name as month \n\
/ (?<title> [^/]+ ) # capture base name as title \n\
\\.html? $ # .htm or .html file ext at end of path ', 'x'
));
Probleme mit Backslashes und mehreren Zeilen verschwinden mit Tagged Templates.
var parts = '/2015/10/Page.html'.match(XRegExp.rx`
^ # match at start of string only
/ (?<year> [^/]+ ) # capture top dir name as year
/ (?<month> [^/]+ ) # capture subdir name as month
/ (?<title> [^/]+ ) # capture base name as title
\.html? $ # .htm or .html file ext at end of path
`);
Zusätzlich erlauben Tagged Templates, Werte v über ${v} einzufügen. Ich würde erwarten, dass eine Bibliothek für reguläre Ausdrücke Strings escapet und reguläre Ausdrücke unverändert einfügt. Zum Beispiel
var str = 'really?';
var regex = XRegExp.rx`(${str})*`;
Dies wäre äquivalent zu
var regex = XRegExp.rx`(really\?)*`;
Beispiel
$`a.${className}[href*='//${domain}/']`
Dies ist eine DOM-Abfrage, die nach allen <a>-Tags sucht, deren CSS-Klasse className ist und deren Ziel eine URL mit der gegebenen Domain ist. Die Tag-Funktion $ stellt sicher, dass die Argumente korrekt escaped werden, was diesen Ansatz sicherer macht als manuelle String-Verkettung.
Facebook React ist „eine JavaScript-Bibliothek zum Erstellen von Benutzeroberflächen“. Es hat die optionale Spracherweiterung JSX, die es Ihnen ermöglicht, *virtuelle* DOM-Bäume für Benutzeroberflächen zu erstellen. Diese Erweiterung macht Ihren Code prägnanter, ist aber auch nicht standardkonform und bricht die Kompatibilität mit dem Rest des JavaScript-Ökosystems.
Die Bibliothek t7.js bietet eine Alternative zu JSX und verwendet Templates, die mit t7 getaggt sind.
t7.module(function(t7) {
function MyWidget(props) {
return t7`
<div>
<span>I'm a widget ${ props.welcome }</span>
</div>
`;
}
t7.assign('Widget', MyWidget);
t7`
<div>
<header>
<Widget welcome="Hello world" />
</header>
</div>
`;
});
In „Why not Template Literals?“ erklärt das React-Team, warum sie sich entschieden haben, keine Template-Literale zu verwenden. Eine Herausforderung ist der Zugriff auf Komponenten innerhalb von Tagged Templates. Zum Beispiel wird MyWidget aus dem zweiten Tagged Template im vorherigen Beispiel zugegriffen. Eine umständliche Möglichkeit, dies zu tun, wäre
<${MyWidget} welcome="Hello world" />
Stattdessen verwendet t7.js eine Registry, die über t7.assign() gefüllt wird. Das erfordert zusätzliche Konfiguration, aber die Template-Literale sehen schöner aus; besonders wenn es sowohl einen öffnenden als auch einen schließenden Tag gibt.
Facebook Relay ist ein „JavaScript-Framework zum Erstellen datengesteuerter React-Anwendungen“. Ein Teil davon ist die Abfragesprache GraphQL, deren Abfragen über Templates erstellt werden können, die mit Relay.QL getaggt sind. Zum Beispiel ( entlehnt von der Relay-Homepage)
class Tea extends React.Component {
render() {
var {name, steepingTime} = this.props.tea;
return (
<li key={name}>
{name} (<em>{steepingTime} min</em>)
</li>
);
}
}
Tea = Relay.createContainer(Tea, {
fragments: { // (A)
tea: () => Relay.QL`
fragment on Tea {
name,
steepingTime,
}
`,
},
});
class TeaStore extends React.Component {
render() {
return <ul>
{this.props.store.teas.map(
tea => <Tea tea={tea} />
)}
</ul>;
}
}
TeaStore = Relay.createContainer(TeaStore, {
fragments: { // (B)
store: () => Relay.QL`
fragment on Store {
teas { ${Tea.getFragment('tea')} },
}
`,
},
});
Die Objekte, beginnend in Zeile A und Zeile B, definieren *Fragmente*, die über Callbacks definiert werden, die Abfragen zurückgeben. Das Ergebnis des Fragments tea wird in this.props.tea eingefügt. Das Ergebnis des Fragments store wird in this.props.store eingefügt.
Dies sind die Daten, auf denen die Abfragen operieren.
const STORE = {
teas: [
{name: 'Earl Grey Blue Star', steepingTime: 5},
···
],
};
Diese Daten sind in einer Instanz von GraphQLSchema verpackt, wo sie den Namen Store erhält (wie in fragment on Store erwähnt).
Dieser Abschnitt beschreibt einen einfachen Ansatz zur Textlokalisierung, der verschiedene Sprachen und verschiedene Locales unterstützt (wie Zahlen, Zeit usw. formatiert werden). Gegeben die folgende Nachricht.
alert(msg`Welcome to ${siteName}, you are visitor
number ${visitorNumber}:d!`);
Die Tag-Funktion msg würde wie folgt funktionieren.
Erstens werden die Literal-Teile zu einem String verkettet, der verwendet werden kann, um eine Übersetzung in einer Tabelle nachzuschlagen. Der Lookup-String für das vorherige Beispiel ist
'Welcome to {0}, you are visitor number {1}!'
Dieser Lookup-String könnte zum Beispiel einer deutschen Übersetzung zugeordnet werden:
'Besucher Nr. {1}, willkommen bei {0}!'
Die englische „Übersetzung“ wäre dieselbe wie der Lookup-String.
Zweitens wird das Ergebnis aus dem Lookup verwendet, um die Substitutionen anzuzeigen. Da ein Lookup-Ergebnis Indizes enthält, kann es die Reihenfolge der Substitutionen ändern. Dies wurde im Deutschen getan, wo die Besuchernummer vor dem Seitennamen steht. Wie die Substitutionen formatiert werden, kann über Annotationen wie :d beeinflusst werden. Diese Annotation bedeutet, dass für visitorNumber ein locale-spezifisches Dezimaltrennzeichen verwendet werden soll. Somit ist ein mögliches englisches Ergebnis
Welcome to ACME Corp., you are visitor number 1,300!
Auf Deutsch haben wir Ergebnisse wie
Besucher Nr. 1.300, willkommen bei ACME Corp.!
Nehmen wir an, wir möchten HTML erstellen, das die folgenden Daten in einer Tabelle anzeigt.
const data = [
{ first: '<Jane>', last: 'Bond' },
{ first: 'Lars', last: '<Croft>' },
];
Wie bereits erklärt, sind Template-Literale keine Templates.
Ein Template ist im Grunde eine Funktion: Daten rein, Text raus. Und diese Beschreibung gibt uns einen Hinweis, wie wir ein Template-Literal in ein tatsächliches Template verwandeln können. Implementieren wir ein Template tmpl als Funktion, die ein Array addrs auf einen String abbildet.
const tmpl = addrs => `
<table>
${addrs.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')}
</table>
`;
console.log(tmpl(data));
// Output:
// <table>
//
// <tr><td><Jane></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td><Croft></td></tr>
//
// </table>
Das äußere Template-Literal liefert die Klammerung <table> und </table>. Darin betten wir JavaScript-Code ein, der einen String durch Verketten eines Arrays von Strings erzeugt. Das Array wird erstellt, indem jeder Eintrag in zwei Tabellenzeilen abgebildet wird. Beachten Sie, dass die einfachen Textstücke <Jane> und <Croft> nicht ordnungsgemäß escaped sind. Wie das über ein Tagged Template geschieht, wird im nächsten Abschnitt erklärt.
Dies ist eine nützliche schnelle Lösung für kleinere Templating-Aufgaben. Für größere Aufgaben möchten Sie möglicherweise leistungsfähigere Lösungen wie die Templating-Engine Handlebars.js oder die in React verwendete JSX-Syntax verwenden.
Anerkennung: Dieser Ansatz zum Text-Templating basiert auf einer Idee von Claus Reinke.
Im Vergleich zur Verwendung von untagged Templates für HTML-Templating, wie wir es im vorherigen Abschnitt getan haben, bieten Tagged Templates zwei Vorteile:
${} mit einem Ausrufezeichen versehen. Dies ist für die Namen erforderlich, die Zeichen enthalten, die escaped werden müssen (<Jane>).join() verbinden, sodass wir diese Methode nicht selbst aufrufen müssen.Dann sieht der Code für das Template wie folgt aus. Der Name der Tag-Funktion ist html.
const tmpl = addrs => html`
<table>
${addrs.map(addr => html`
<tr><td>!${addr.first}</td></tr>
<tr><td>!${addr.last}</td></tr>
`)}
</table>
`;
const data = [
{ first: '<Jane>', last: 'Bond' },
{ first: 'Lars', last: '<Croft>' },
];
console.log(tmpl(data));
// Output:
// <table>
//
// <tr><td><Jane></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td><Croft></td></tr>
//
// </table>
Beachten Sie, dass die spitzen Klammern um Jane und Croft escaped sind, während die um tr und td nicht.
Wenn Sie eine Substitution mit einem Ausrufezeichen (!${addr.first}) versehen, wird sie HTML-escaped. Die Tag-Funktion prüft den Text vor einer Substitution, um zu bestimmen, ob sie escaped oder nicht.
Eine Implementierung von html wird später gezeigt.
Das Folgende ist ein Tagged Template-Literal.
tagFunction`lit1\n${subst1} lit2 ${subst2}`
Dieses Literal löst (ungefähr) den folgenden Funktionsaufruf aus:
tagFunction(['lit1\n', ' lit2 ', ''], subst1, subst2)
Der exakte Funktionsaufruf sieht eher so aus:
// Globally: add template object to per-realm template map
{
// “Cooked” template strings: backslash is interpreted
const templateObject = ['lit1\n', ' lit2 ', ''];
// “Raw” template strings: backslash is verbatim
templateObject.raw = ['lit1\\n', ' lit2 ', ''];
// The Arrays with template strings are frozen
Object.freeze(templateObject.raw);
Object.freeze(templateObject);
__templateMap__[716] = templateObject;
}
// In-place: invocation of tag function
tagFunction(__templateMap__[716], subst1, subst2)
Es gibt zwei Arten von Eingaben, die die Tag-Funktion erhält:
' lit2 '). Ein Template-Objekt speichert zwei Versionen der Template-Strings.\n. Gespeichert in templateObject[0] usw.templateObject.raw[0] usw.${} in Template-Literale eingebettet werden (z. B. subst1). Substitutionen sind dynamisch, sie können sich bei jeder Ausführung ändern.Die Idee hinter einem globalen Template-Objekt ist, dass dasselbe Tagged Template mehrmals ausgeführt werden kann (z. B. in einer Schleife oder einer Funktion). Das Template-Objekt ermöglicht es der Tag-Funktion, Daten aus früheren Aufrufen zu cachen: Sie kann Daten, die sie aus Eingabeart #1 (Template-Strings) abgeleitet hat, in das Objekt legen, um eine Neuberechnung zu vermeiden. Caching erfolgt pro *Realm* (stellen Sie sich einen Frame in einem Browser vor). Das heißt, es gibt ein Template-Objekt pro Aufrufstelle und Realm.
Verwenden wir die folgende Tag-Funktion, um zu untersuchen, wie viele Template-Strings es im Vergleich zu Substitutionen gibt.
function tagFunc(templateObject, ...substs) {
return { templateObject, substs };
}
Die Anzahl der Template-Strings ist immer eins mehr als die Anzahl der Substitutionen. Mit anderen Worten: Jede Substitution ist immer von zwei Template-Strings umgeben.
templateObject.length === substs.length + 1
Wenn eine Substitution zuerst in einem Literal steht, wird sie von einem leeren Template-String vorangestellt.
> tagFunc`${'subst'}xyz`
{ templateObject: [ '', 'xyz' ], substs: [ 'subst' ] }
Wenn eine Substitution zuletzt in einem Literal steht, wird sie von einem leeren Template-String angehängt.
> tagFunc`abc${'subst'}`
{ templateObject: [ 'abc', '' ], substs: [ 'subst' ] }
Ein leeres Template-Literal erzeugt einen Template-String und keine Substitutionen.
> tagFunc``
{ templateObject: [ '' ], substs: [] }
Template-Strings sind in zwei Interpretationen verfügbar – „cooked“ und „raw“. Diese Interpretationen beeinflussen das Escaping.
\) vor ${, dass es als Beginn einer Substitution interpretiert wird.Die Tag-Funktion describe erlaubt uns zu untersuchen, was das bedeutet.
function describe(tmplObj, ...substs) {
return {
Cooked: merge(tmplObj, substs),
Raw: merge(tmplObj.raw, substs),
};
}
function merge(tmplStrs, substs) {
// There is always at least one element in tmplStrs
let result = tmplStrs[0];
substs.forEach((subst, i) => {
result += String(subst);
result += tmplStrs[i+1];
});
return result;
}
Verwenden wir diese Tag-Funktion:
> describe`${3+3}`
{ Cooked: '6', Raw: '6' }
> describe`\${3+3}`
{ Cooked: '${3+3}', Raw: '\\${3+3}' }
> describe`\\${3+3}`
{ Cooked: '\\6', Raw: '\\\\6' }
> describe`\``
{ Cooked: '`', Raw: '\\`' }
Wie Sie sehen können, gibt es, wenn die gekochte Interpretation eine Substitution oder einen Backtick hat, auch in der rohen Interpretation eine. Allerdings erscheinen alle Backslashes aus dem Literal in der rohen Interpretation.
Andere Vorkommen des Backslash werden wie folgt interpretiert:
Zum Beispiel
> describe`\\`
{ Cooked: '\\', Raw: '\\\\' }
> describe`\n`
{ Cooked: '\n', Raw: '\\n' }
> describe`\u{58}`
{ Cooked: 'X', Raw: '\\u{58}' }
Zusammenfassend lässt sich sagen: Die einzige Auswirkung, die der Backslash im rohen Modus hat, ist, dass er Substitutionen und Backticks escapet.
String.raw So würden Sie String.raw implementieren.
function raw(strs, ...substs) {
let result = strs.raw[0];
for (const [i,subst] of substs.entries()) {
result += subst;
result += strs.raw[i+1];
}
return result;
}
Ich habe zuvor die Tag-Funktion html für HTML-Templating demonstriert.
const tmpl = addrs => html`
<table>
${addrs.map(addr => html`
<tr><td>!${addr.first}</td></tr>
<tr><td>!${addr.last}</td></tr>
`)}
</table>
`;
const data = [
{ first: '<Jane>', last: 'Bond' },
{ first: 'Lars', last: '<Croft>' },
];
console.log(tmpl(data));
// Output:
// <table>
//
// <tr><td><Jane></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td><Croft></td></tr>
//
// </table>
Wenn Sie einer Substitution ein Ausrufezeichen voranstellen (!${addr.first}), wird sie HTML-escaped. Die Tag-Funktion prüft den Text vor einer Substitution, um zu bestimmen, ob sie escaped oder nicht.
Dies ist eine Implementierung von html.
function html(templateObject, ...substs) {
// Use raw template strings: we don’t want
// backslashes (\n etc.) to be interpreted
const raw = templateObject.raw;
let result = '';
substs.forEach((subst, i) => {
// Retrieve the template string preceding
// the current substitution
let lit = raw[i];
// In the example, map() returns an Array:
// If `subst` is an Array (and not a string),
// we turn it into a string
if (Array.isArray(subst)) {
subst = subst.join('');
}
// If the substitution is preceded by an exclamation
// mark, we escape special characters in it
if (lit.endsWith('!')) {
subst = htmlEscape(subst);
lit = lit.slice(0, -1);
}
result += lit;
result += subst;
});
// Take care of last template string
result += raw[raw.length-1]; // (A)
return result;
}
Es gibt immer eine Substitution mehr als Template-Strings, weshalb wir den letzten Template-String in Zeile A anhängen müssen.
Die folgende ist eine einfache Implementierung von htmlEscape().
function htmlEscape(str) {
return str.replace(/&/g, '&') // first!
.replace(/>/g, '>')
.replace(/</g, '<')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/`/g, '`');
}
Es gibt noch mehr Dinge, die Sie mit diesem Ansatz zum Templating tun können.
cond?then:else) oder über den logischen ODER-Operator (||) erfolgen. !${addr.first ? addr.first : '(No first name)'}
!${addr.first || '(No first name)'}
const theHtml = html`
<div>
Hello!
</div>`;
Die ersten Nicht-Leerzeichen sind <div>, was bedeutet, dass der Text in Spalte 4 beginnt (die linkeste Spalte ist Spalte 0). Die Tag-Funktion html könnte alle vorhergehenden Spalten automatisch entfernen. Dann wäre das vorherige Tagged Template äquivalent zu
const theHtml =
html`<div>
Hello!
</div>`;
// Without destructuring
${addrs.map((person) => html`
<tr><td>!${person.first}</td></tr>
<tr><td>!${person.last}</td></tr>
`)}
// With destructuring
${addrs.map(({first,last}) => html`
<tr><td>!${first}</td></tr>
<tr><td>!${last}</td></tr>
`)}
Es gibt zwei Möglichkeiten, Instanzen von regulären Ausdrücken zu erstellen.
/^abc$/iRegExp: new RegExp('^abc$', 'i')Wenn Sie letzteres verwenden, dann weil Sie bis zur Laufzeit warten müssen, damit alle notwendigen Zutaten verfügbar sind. Sie erstellen den regulären Ausdruck durch Verketten von drei Arten von Teilen:
Für #3 müssen Sonderzeichen (Punkte, eckige Klammern usw.) escaped werden, während #1 und #2 unverändert verwendet werden können. Eine Tag-Funktion für reguläre Ausdrücke regex kann bei dieser Aufgabe helfen.
const INTEGER = /\d+/;
const decimalPoint = '.'; // locale-specific! E.g. ',' in Germany
const NUMBER = regex`${INTEGER}(${decimalPoint}${INTEGER})?`;
regex sieht so aus:
function regex(tmplObj, ...substs) {
// Static text: verbatim
let regexText = tmplObj.raw[0];
for ([i, subst] of substs.entries()) {
if (subst instanceof RegExp) {
// Dynamic regular expressions: verbatim
regexText += String(subst);
} else {
// Other dynamic data: escaped
regexText += quoteText(String(subst));
}
// Static text: verbatim
regexText += tmplObj.raw[i+1];
}
return new RegExp(regexText);
}
function quoteText(text) {
return text.replace(/[\\^$.*+?()[\]{}|=!<>:-]/g, '\\$&');
}
Template-Literale und Tagged Template-Literale wurden aus der Sprache E übernommen, die diese Funktion quasi literals nennt.
Makros ermöglichen es Ihnen, Sprachkonstrukte mit benutzerdefinierter Syntax zu implementieren. Es ist schwierig, Makros für eine Programmiersprache bereitzustellen, deren Syntax so komplex ist wie die von JavaScript. Die Forschung in diesem Bereich läuft (siehe Mozillas sweet.js).
Während Makros für die Implementierung von Subsprachen viel leistungsfähiger sind als Tagged Templates, hängen sie von der Tokenisierung der Sprache ab. Daher sind Tagged Templates komplementär, da sie sich auf Textinhalte spezialisieren.
Was, wenn ich ein Template-Literal wie `Hallo ${name}!` aus einer externen Quelle (z. B. einer Datei) laden möchte?
Sie missbrauchen Template-Literale, wenn Sie dies tun. Da ein Template-Literal beliebige Ausdrücke enthalten kann und ein Literal ist, ist das Laden von irgendwo anders ähnlich wie das Laden eines Ausdrucks oder eines String-Literals – Sie müssen eval() oder etwas Ähnliches verwenden.
Der Backtick war eines der wenigen ASCII-Zeichen, die in JavaScript noch nicht verwendet wurden. Die Syntax ${} für Interpolation ist sehr verbreitet (Unix-Shells usw.).
Die Terminologie für Template-Literale änderte sich relativ spät während der Erstellung der ES6-Spezifikation. Die folgenden sind die alten Begriffe: