\n
)String.raw
ES6には、テンプレートリテラルとタグ付きテンプレートリテラルという2種類の新しいリテラルがあります。これら2つのリテラルは名前と見た目が似ていますが、かなり異なります。そのため、区別することが重要です。
テンプレートリテラルは、複数行にまたがり、インターポーレートされた式(${···}
を使用して挿入)を含むことができる文字列リテラルです。
const
firstName
=
'Jane'
;
console
.
log
(
`Hello
${
firstName
}
!
How are you
today?`
);
// Output:
// Hello Jane!
// How are you
// today?
タグ付きテンプレートリテラル(略してタグ付きテンプレート)は、テンプレートリテラルの前に関数を記述することで作成されます。
> String.raw`A \tagged\ template`
'A \\tagged\\ template'
タグ付きテンプレートは関数呼び出しです。前の例では、String.raw
メソッドが呼び出されて、タグ付きテンプレートの結果が生成されています。
リテラルは、値を生成する構文上の構成要素です。文字列リテラル(文字列を生成する)や正規表現リテラル(正規表現オブジェクトを生成する)などが例として挙げられます。ECMAScript 6には、2つの新しいリテラルがあります。
テンプレートリテラルとタグ付きテンプレートの名前はやや誤解を招きやすいことに注意することが重要です。Web開発でよく使用されるテンプレート(例:JSONデータで埋め込むことができる空欄のあるテキストファイル)とは関係ありません。
テンプレートリテラルは、複数行にまたがり、式をインターポーレート(その結果を含める)できる新しい種類の文字列リテラルです。例えば
const
firstName
=
'Jane'
;
console
.
log
(
`Hello
${
firstName
}
!
How are you
today?`
);
// Output:
// Hello Jane!
// How are you
// today?
リテラル自体はバッククォート(`
)で区切られ、リテラル内のインターポーレートされた式は${
と}
で区切られます。テンプレートリテラルは常に文字列を生成します。
バックスラッシュはテンプレートリテラル内でのエスケープに使用されます。
これにより、テンプレートリテラル内でバッククォートと${
を記述できます。
> `\``
'`'
> `$` // OK
'$'
> `${
`
SyntaxError
>
`\${`
'${'
>
`
\$
{
}
`
'${}'
それ以外は、バックスラッシュは文字列リテラルと同様に機能します。
> `\\`
'\\'
> `\n`
'\n'
> `\u{58}`
'X'
\n
) 改行を終了する一般的な方法は次のとおりです。
\n
、U+000A):Unix(現在のmacOSを含む)で使用されます。\r
、U+000D):旧Mac OSで使用されていました。\r\n
):Windowsで使用されます。これらの改行文字はすべて、テンプレートリテラルでLFに正規化されます。つまり、次のコードはすべてのプラットフォームでtrue
を出力します。
const
str
=
`BEFORE
AFTER`
;
console
.
log
(
str
===
'BEFORE\nAFTER'
);
// true
以下はタグ付きテンプレートリテラル(略してタグ付きテンプレート)です。
tagFunction
`Hello
${
firstName
}
${
lastName
}
!`
式の後にテンプレートリテラルを配置すると、パラメータリスト(括弧内のコンマ区切りの値)が関数呼び出しをトリガーする方法と同様に、関数呼び出しがトリガーされます。(実際には、最初のパラメータは単なる配列以上のものですが、それは後で説明します)。
tagFunction
([
'Hello '
,
' '
,
'!'
],
firstName
,
lastName
)
したがって、バッククォート内のコンテンツの前にある名前は、呼び出す関数の名前であるタグ関数です。タグ関数は、2種類の異なるデータを受け取ります。
'Hello '
などのテンプレート文字列。firstName
などの置換(${}
で区切られる)。置換は任意の式にすることができます。テンプレート文字列は静的に(コンパイル時に)知られていますが、置換は実行時にのみ知られています。タグ関数は、そのパラメータを自由に処理できます。テンプレート文字列を完全に無視したり、任意の種類の値を返したりすることができます。
さらに、タグ関数は各テンプレート文字列の2つのバージョンを取得します。
`\n`
は'\\n'
、長さ2の文字列になります)。`\n`
は改行のみを含む文字列になります)。これにより、String.raw
(後で説明します)が機能します。
> String.raw`\n` === '\\n'
true
タグ付きテンプレートリテラルを使用すると、JavaScriptがほとんどのパース処理を行うため、カスタムの埋め込みサブ言語(ドメイン固有言語と呼ばれることもあります)を簡単に実装できます。結果を受け取る関数を作成するだけで済みます。
例を見てみましょう。その一部は、テンプレートリテラルの元の提案(それらを古い名前である準リテラルで参照しています)に触発されています。
ES6には、バックスラッシュに特別な意味がないロー文字列用のタグ関数String.raw
が含まれています。
const
str
=
String
.
raw
`This is a text
with multiple lines.
Escapes are not interpreted,
\
n is not a newline.`
;
これは、バックスラッシュを含む文字列を作成する必要がある場合に便利です。例えば
function
createNumberRegExp
(
english
)
{
const
PERIOD
=
english
?
String
.
raw
`
\
.`
:
','
;
// (A)
return
new
RegExp
(
`[0-9]+(
${
PERIOD
}
[0-9]+)?`
);
}
行Aでは、String.raw
を使用することで、正規表現リテラルのようにバックスラッシュを書くことができます。通常の文字列リテラルでは、2回エスケープする必要があります。まず、正規表現のドットをエスケープする必要があります。次に、文字列リテラルのバックスラッシュをエスケープする必要があります。
const
proc
=
sh
`ps ax | grep
${
pid
}
`
;
(出典:David Herman)
const
buffer
=
bytes
`455336465457210a`
;
(出典:David Herman)
POST
`http://foo.org/bar?a=
${
a
}
&b=
${
b
}
Content-Type: application/json
X-Credentials:
${
credentials
}
{ "foo":
${
foo
}
,
"bar":
${
bar
}
}
`
(
myOnReadyStateChangeHandler
);
(出典:Luke Hoban)
Steven Levithanは、例を挙げて、タグ付きテンプレートリテラルを彼の正規表現ライブラリXRegExpで使用する方法を示しています。
タグ付きテンプレートを使用しない場合、次のコードのように記述します。
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
XRegExpにより、名前付きグループ(year
、month
、title
)とx
フラグが得られることがわかります。このフラグを使用すると、ほとんどの空白は無視され、コメントを挿入できます。
文字列リテラルがここでうまく機能しない理由は2つあります。まず、文字列リテラルに対してエスケープするために、すべての正規表現のバックスラッシュを2回入力する必要があります。次に、複数行を入力することが面倒です。
文字列を追加する代わりに、現在の行をバックスラッシュで終了すると、次の行で文字列リテラルを続けることもできます。しかし、それでも、特に各行の最後に\n
による明示的な改行が必要なため、視覚的な混乱が多くあります。
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'
));
バックスラッシュと複数行の問題は、タグ付きテンプレートで解消されます。
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
`
);
さらに、タグ付きテンプレートでは、${v}
を使用して値v
を挿入できます。正規表現ライブラリは、文字列をエスケープし、正規表現をそのまま挿入するものと予想します。例えば
var
str
=
'really?'
;
var
regex
=
XRegExp
.
rx
`(
${
str
}
)*`
;
これは、次のものと同等です。
var
regex
=
XRegExp
.
rx
`(really
\
?)*`
;
例
$
`a.
${
className
}
[href*='//
${
domain
}
/']`
これは、CSSクラスがclassName
で、ターゲットが指定されたドメインを持つURLであるすべての<a>
タグを検索するDOMクエリです。タグ関数$
は、引数が正しくエスケープされるようにするため、手動での文字列連結よりも安全なアプローチになります。
Facebook Reactは、「ユーザーインターフェースを構築するためのJavaScriptライブラリ」です。ユーザーインターフェースの仮想DOMツリーを構築できるようにするオプションの言語拡張JSXがあります。この拡張機能により、コードが簡潔になりますが、非標準であり、JavaScriptエコシステムの残りの部分との互換性が損なわれます。
ライブラリt7.jsはJSXの代替手段を提供し、t7
でタグ付けされたテンプレートを使用します。
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>
`
;
});
「テンプレートリテラルを使用しない理由」において、Reactチームはなぜテンプレートリテラルを使用しない選択をしたのかを説明しています。課題の1つは、タグ付きテンプレート内でコンポーネントにアクセスすることです。例えば、前の例の2番目のタグ付きテンプレートではMyWidget
にアクセスします。冗長な方法としては、以下のように行うことができます。
<
$
{
MyWidget
}
welcome
=
"Hello world"
/>
代わりに、t7.jsはt7.assign()
を介して埋められるレジストリを使用します。これは追加の設定が必要になりますが、テンプレートリテラルはより見栄えが良くなります。特に、開始タグと終了タグの両方がある場合です。
Facebook Relayは、「データ駆動型のReactアプリケーションを構築するためのJavaScriptフレームワーク」です。その一部として、クエリ言語GraphQLがあり、そのクエリはRelay.QL
でタグ付けされたテンプレートを介して作成できます。例(Relayのホームページから借用)
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'
)
}
},
}
`
,
},
});
A行とB行から始まるオブジェクトはフラグメントを定義しており、クエリを返すコールバックを介して定義されます。フラグメントtea
の結果はthis.props.tea
に入れられます。フラグメントstore
の結果はthis.props.store
に入れられます。
これがクエリが操作するデータです。
const
STORE
=
{
teas
:
[
{
name
:
'Earl Grey Blue Star'
,
steepingTime
:
5
},
···
],
};
このデータはGraphQLSchema
のインスタンスにラップされ、そこでfragment on Store
で言及されているようにStore
という名前が付けられます。
このセクションでは、異なる言語と異なるロケール(数値、時刻などのフォーマット方法)をサポートする、テキストローカリゼーションの簡単なアプローチについて説明します。次のメッセージがあるとします。
alert
(
msg
`Welcome to
${
siteName
}
, you are visitor
number
${
visitorNumber
}
:d!`
);
タグ関数msg
は次のように機能します。
まず、リテラルの部分を連結して、テーブルで翻訳を検索するために使用できる文字列を作成します。前の例の検索文字列は次のとおりです。
'Welcome to {0}, you are visitor number {1}!'
この検索文字列は、例えば、ドイツ語の翻訳にマッピングできます。
'Besucher Nr. {1}, willkommen bei {0}!'
英語の「翻訳」は、検索文字列と同じになります。
次に、検索の結果を使用して置換を表示します。検索結果にはインデックスが含まれているため、置換の順序を並べ替えることができます。これはドイツ語で行われており、訪問者数はサイト名の前に来ます。置換のフォーマット方法は、:d
などのアノテーションで影響を受ける可能性があります。このアノテーションは、visitorNumber
にはロケール固有の小数点セパレータを使用する必要があることを意味します。したがって、考えられる英語の結果は次のとおりです。
Welcome to ACME Corp., you are visitor number 1,300!
ドイツ語では、次のような結果になります。
Besucher Nr. 1.300, willkommen bei ACME Corp.!
次のデータをテーブルに表示するHTMLを作成したいとしましょう。
const
data
=
[
{
first
:
'<Jane>'
,
last
:
'Bond'
},
{
first
:
'Lars'
,
last
:
'<Croft>'
},
];
前述のように、テンプレートリテラルはテンプレートではありません。
テンプレートは基本的に関数です。入力はデータ、出力はテキストです。そして、その説明は、テンプレートリテラルを実際のテンプレートに変換する方法の手がかりを与えてくれます。配列addrs
を文字列にマッピングする関数としてテンプレートtmpl
を実装してみましょう。
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>
外側のテンプレートリテラルは、ブラケット<table>
と</table>
を提供します。内部では、文字列の配列を結合することによって文字列を生成するJavaScriptコードを埋め込んでいます。配列は、各アドレスを2つのテーブル行にマッピングすることによって作成されます。プレーンテキストの部分<Jane>
と<Croft>
は適切にエスケープされていません。タグ付きテンプレートを介してそれを行う方法は、次のセクションで説明します。
これは、小規模なテンプレート化タスクのための便利な迅速なソリューションです。大規模なタスクの場合は、Handlebars.jsなどのより強力なソリューションや、Reactで使用されているJSX構文を使用することを検討してください。
謝辞:このテキストテンプレート化へのアプローチは、Claus Reinkeによるアイデアに基づいています。
前のセクションで行ったように、HTMLテンプレート化にタグなしテンプレートを使用する場合と比較して、タグ付きテンプレートには2つの利点があります。
${}
の前に感嘆符を付けることで、文字をエスケープできます。これは、エスケープが必要な文字(<Jane>
)を含む名前には必要です。join()
できるため、自分でそのメソッドを呼び出す必要がありません。テンプレートのコードは次のようになります。タグ関数の名前は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>
Jane
とCroft
の周りの山かっこはエスケープされていますが、tr
とtd
の周りの山かっこはエスケープされていません。
置換の前に感嘆符(!${addr.first}
)を付けると、HTMLエスケープされます。タグ関数は、置換の前にあるテキストをチェックして、エスケープするかどうかを判断します。
html
の実装は後で示します。
以下は、タグ付きテンプレートリテラルです。
tagFunction
`lit1
\
n
${
subst1
}
lit2
${
subst2
}
`
このリテラルは、(おおよそ)次の関数呼び出しをトリガーします。
tagFunction
([
'lit1\n'
,
' lit2 '
,
''
],
subst1
,
subst2
)
正確な関数呼び出しは、次のようになります。
// 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
)
タグ関数が受信する入力には2種類あります。
' lit2 '
)。テンプレートオブジェクトはテンプレート文字列の2つのバージョンを格納します。\n
などのエスケープが解釈されたもの。templateObject[0]
などに格納されます。templateObject.raw[0]
などに格納されます。${}
を介してテンプレートリテラルに埋め込まれた値(例:subst1
)。置換は動的であり、呼び出しごとに変更できます。グローバルテンプレートオブジェクトの背後にある考え方は、同じタグ付きテンプレートが複数回実行される可能性がある(例:ループ内または関数内)ことです。テンプレートオブジェクトにより、タグ関数は以前の呼び出しからのデータをキャッシュできます。入力の種類1(テンプレート文字列)から派生したデータをオブジェクトに格納して、再計算を回避できます。キャッシュはレルムごとに実行されます(ブラウザのフレームと考えてください)。つまり、呼び出しサイトとレルムごとに1つのテンプレートオブジェクトがあります。
次のタグ関数を使用して、テンプレート文字列の数と置換の数を比較してみましょう。
function
tagFunc
(
templateObject
,
...
substs
)
{
return
{
templateObject
,
substs
};
}
テンプレート文字列の数は、常に置換の数プラス1です。言い換えれば、すべての置換は常に2つのテンプレート文字列で囲まれています。
templateObject
.
length
===
substs
.
length
+
1
置換がリテラルの先頭にある場合、空のテンプレート文字列が接頭辞として付けられます。
> tagFunc`${
'subst'
}
xyz`
{ templateObject: [ '', 'xyz' ], substs: [ 'subst' ] }
置換がリテラルの最後にある場合、空のテンプレート文字列が接尾辞として付けられます。
> tagFunc`abc${
'subst'
}
`
{ templateObject: [ 'abc', '' ], substs: [ 'subst' ] }
空のテンプレートリテラルは、1つのテンプレート文字列と置換を生成しません。
> tagFunc``
{ templateObject: [ '' ], substs: [] }
テンプレート文字列は、調理済みと未加工の2つの解釈で使用できます。これらの解釈はエスケープに影響します。
${
の前にあるバックスラッシュ(\
)は、置換の開始として解釈されるのを防ぎます。タグ関数describe
を使用すると、それが何を意味するのかを調べることができます。
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
;
}
このタグ関数を使用してみましょう。
> 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: '\\`' }
ご覧のように、調理済み解釈に置換またはバッククォートがある場合は、未加工解釈にもあります。ただし、リテラルからのすべてのバックスラッシュは、未加工解釈に表示されます。
バックスラッシュの他の出現は、次のように解釈されます。
例えば
> describe`\\`
{ Cooked: '\\', Raw: '\\\\' }
> describe`\n`
{ Cooked: '\n', Raw: '\\n' }
> describe`\u{58}`
{ Cooked: 'X', Raw: '\\u{58}' }
要約すると、未加工モードでのバックスラッシュの唯一の効果は、置換とバッククォートをエスケープすることです。
String.raw
String.raw
の実装方法は次のとおりです。
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
;
}
前に、HTMLテンプレート化のためのタグ関数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>
置換の前に感嘆符(!${addr.first}
)を付けると、HTMLエスケープされます。タグ関数は、置換の前にあるテキストをチェックして、エスケープするかどうかを判断します。
これは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
;
}
置換よりも常に1つ多いテンプレート文字列があるため、A行で最後のテンプレート文字列を追加する必要があります。
以下は、htmlEscape()
の簡単な実装です。
function
htmlEscape
(
str
)
{
return
str
.
replace
(
/&/g
,
'&'
)
// first!
.
replace
(
/>/g
,
'>'
)
.
replace
(
/</g
,
'<'
)
.
replace
(
/"/g
,
'"'
)
.
replace
(
/'/g
,
'''
)
.
replace
(
/`/g
,
'`'
);
}
このテンプレート化アプローチでは、他にもできることがあります。
cond?then:else
)または論理和演算子(||
)を使用して行うことができます。
!
$
{
addr
.
first
?
addr
.
first
:
'(No first name)'
}
!
$
{
addr
.
first
||
'(No first name)'
}
const
theHtml
=
html
`
<div>
Hello!
</div>`
;
最初の非空白文字は<div>
であり、これはテキストが4列目(左端の列が0列目)から始まることを意味します。タグ関数html
は、先行するすべての列を自動的に削除できます。すると、前のタグ付きテンプレートは次のようになります。
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>
`
)}
正規表現インスタンスを作成するには、2つの方法があります。
/^abc$/i
RegExp
コンストラクターを使用して:new RegExp('^abc$', 'i')
後者を使用する場合は、必要なすべての要素が利用可能になるまで実行時まで待つ必要があるためです。3種類の要素を連結して正規表現を作成しています。
#3の場合、特殊文字(ドット、角括弧など)をエスケープする必要がありますが、#1と#2はそのまま使用できます。正規表現タグ関数regex
がこのタスクに役立ちます。
const
INTEGER
=
/\d+/
;
const
decimalPoint
=
'.'
;
// locale-specific! E.g. ',' in Germany
const
NUMBER
=
regex
`
${
INTEGER
}
(
${
decimalPoint
}${
INTEGER
}
)?`
;
regex
は次のようになります。
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
,
'\\$&'
);
}
テンプレートリテラルとタグ付きテンプレートリテラルは、E言語から借用されたもので、この機能を準リテラルと呼んでいます。
マクロを使用すると、カスタム構文を持つ言語構成要素を実装できます。JavaScriptのように構文が複雑なプログラミング言語のマクロを提供することは困難です。この分野の研究は現在進行中です(Mozillaのsweet.jsを参照)。
マクロは、タグ付きテンプレートよりもサブ言語の実装にはるかに強力ですが、言語のトークン化に依存します。したがって、タグ付きテンプレートはテキストコンテンツを専門とするため、補完的なものです。
`Hello ${name}!`
のようなテンプレートリテラルを外部ソース(例:ファイル)から読み込む場合はどうすればよいですか?
そのようなことをすると、テンプレートリテラルを誤用しています。テンプレートリテラルには任意の式を含めることができ、リテラルであるため、他の場所から読み込むことは、式または文字列リテラルを読み込むことと同様であり、eval()
または同様の方法を使用する必要があります。
バックティックは、JavaScriptでまだ使用されていなかった少数のASCII文字の1つでした。補間のための構文${}
は非常に一般的です(Unixシェルなど)。
テンプレートリテラルという用語は、ES6仕様の作成中に比較的遅く変更されました。古い用語を次に示します。