はじめに
今回はJavaScriptにおけるテンプレートリテラルについて、基本構造から発展的な内容までを確認していく記事です。
話は変わりますが、React(JavaScriptのライブラリ)の勉強をするなかで、Styled Component というスタイルを定義するためのライブラリを知りました。
その記載例が以下です。
const MyComp = styled.div`
width: 200px;
margin: 5px;
border: 1px solid blue;
`
正直、これを見たとき面食らいました。
バッククォートが使われているのでテンプレートリテラルだということはわかりましたが、これによって何をしているのかは全くわかりませんでした。
まだまだ知らないことが多すぎると感じたため、改めてテンプレートリテラルについて学び直し、JavaScriptの基礎を固めるべく、本記事の執筆に至りました。
では、早速基本から確認していきましょう。
テンプレートリテラルとは
テンプレートリテラルとはリテラル(ソースコード上の文字列とか数字とか)の記載形式です。
通常、JavaScriptでは文字列を表現する方法として''
(シングルクォート)か""
(ダブルクォート)を用いますが、テンプレートリテラルは``
(バッククォート)を使用します。
※なお、バッククォートはShift
+ @
で入力できます。
テンプレートリテラルの基本
テンプレートリテラルはただの文字列も表せますが、改行をそのまま出力したり、テンプレートリテラル内に式や変数を埋め込んだりすることができます。
// 文字列
console.log(`Hello world`)
// 改行
console.log(`Hello
world`);
// 式
console.log(`2 + 5 equal ${2 + 5}`);
// 変数
const person = 'JS Taro';
console.log(`Hello ${person}`);
変数や式を埋め込むためには、テンプレートリテラル内で使いたい式や変数を${}
で囲みます。
これをプレースホルダといいます。
ちなみに、テンプレートリテラルを使わずに、シングルクォート(通常の文字列)で同じように表現すると以下のようになります。
console.log('Hello\nworld');
console.log(`2 + 5 equal ` + (2 + 5));
console.log(`Hello ` + person);
シングルクォートの場合はいちいち+
をつかって連結する必要があるため、冗長になります。
テンプレートリテラルを使用したほうがすっきり直感的でわかりやすくなりますね。
テンプレートリテラルの発展
さきほどまで確認してきたテンプレートリテラルは、テンプレートリテラルの中でもタグ無しテンプレートと呼ばれるものになります。
テンプレートリテラルの発展として、タグ付きテンプレートを確認します。
タグ付きテンプレートを使うことで、文字列の生成だけでなく自分で様々なカスタマイズをすることができます。
タグ付きテンプレートの基本形
タグ付きテンプレートは以下のように使用します。
function myTag(strings, ...values) {
let result = '';
for (let i = 0; i < strings.length; i++) {
result += strings[i];
if (i < values.length) {
result += values[i];
}
}
return result;
}
const person = 'JS Taro'
const output = myTag`Hello ${person}`;
console.log(output);
いきなりぐっと難しくなった感じがありますが、一つずつ確認していきます。
- タグ付きテンプレートの宣言方法
- タグ関数の引数
この2つが理解できれば、タグ付きテンプレートをマスターできます。
宣言方法
タグ名`テンプレートリテラル`
今まで使用してきたテンプレートリテラルの前にタグを置きます。
タグには大抵関数が使用されるので、関数名を書くことがほとんどです。
独自で作成した処理をタグとして指定します。
タグがある以外はタグ無しテンプレートと書き方は同じです。
タグ関数の引数
タグに使用されるのはほぼ関数であるため、タグに指定する関数について確認します。
関数には大きく分けて2つの引数がわたってきます。
function 関数名(strings, ...values) {
// do something
}
strings
まず1つめはテンプレートリテラル内の値(strings
)です。これは配列です。変数名は任意ですが、strings
とすることが慣例のようです。
テンプレートリテラル内の値をプレースホルダごとに区切ったものが配列の要素となります。
例えば
myTag`Hello ${person}`
という基本形で示した例のようなテンプレートリテラルの場合、
プレースホルダは1つなので、strings
の要素数は2つです。
「1つでは?」と思うかもしれませんが、${person}
の直後に''
(空文字)があると考えます。
よって、strings
の中身は['Hello ', '']
となります。
values
2つめにタグ関数の引数として設定されるのはプレースホルダの値です。
これはプレースホルダを記載した個数分、引数が設定されます。
そのため、以下のように1つずつ定義することもできます。
myTag`${'a'}${'b'}${'c'}`;
// 3つのプレースホルダに対して、arg1, arg2, arg3の3つの引数を設定
function myTag(strings, arg1, arg2, arg3) {
// do something
}
しかし、大抵は基本形の例で示したように...values
の形で受け取ります。
values
の部分は任意の変数名でよいです。変数名の頭に...
をつけ、...変数名
とします。変数を使用するときには...
は不要です。
これは残余引数(rest parameters) と呼ばれるもので、引数に設定された複数の値をまとめて1つの配列として受け取ることができます。
なお、残余引数はタグ関数の中だけではなく、どんな関数にも使用できます。
もっと詳しく知りたい方は「残余引数」で調べるといろいろ出てきますので見てみてください。
基本形の例を読み解く
タグ付きテンプレートの宣言方法、およびタグ関数の引数について理解できたでしょうか。
改めて、タグ付きテンプレートの基本形でやっていたことを確認してみます。
function myTag(strings, ...values) {
let result = '';
for (let i = 0; i < strings.length; i++) {
result += strings[i];
if (i < values.length) {
result += values[i];
}
}
return result;
}
const person = 'JS Taro'
const output = myTag`Hello ${person}`;
console.log(output);
まず
myTag`Hello ${person}`
でタグとしてmyTag
関数を使うことを宣言しています。
そしてmyTag
関数の引数には以下の2つが設定されます。
-
strings
テンプレートリテラル内の値をプレースホルダごとに分割した配列である['Hello ', '']
-
...values
プレースホルダの配列である[person]
そして、for文をstrings
の要素数分、繰り返します。
strings
の要素とvalues
の要素を順に結合し、結合した結果を戻り値として返却しています。
なお、strings
の要素数は、必ずvalues
の要素数より1つ多くなるため、for文の最後のループではif文の条件がfalseとなり、values
の要素の結合は実施されません。
手のどちらか片方をテンプレートリテラルと考えてみてください。
指の股がプレースホルダ(values
)とすると、指はプレースホルダごとに区切られた要素(strings
)であり、その数は必ずプレースホルダより1つ多くなります。
少し話がそれましたが、以上のことから、基本形の例で示したmyTag
関数はテンプレートリテラルで指定された値とプレースホルダを結合する処理を行っていたのでした。
つまり、タグ無しテンプレートと同じ挙動を、あえてタグ付きテンプレートで再現していたということです。
タグ付きテンプレートの応用
文字列以外の戻り値
基本形の例では、テンプレートリテラル内の値を順に結合した値を返却していましたが、タグの処理は文字列を戻り値として返さなくても成立します。
function myTag(strings, ...values) {
let obj = { Strings: {}, Values: {} }
for (let i = 0; i < strings.length; i++) {
obj.Strings[`str_${i}`] = strings[i];
if (i < values.length) {
obj.Values[`val_${i}`] = values[i];
}
}
return obj;
}
const person = 'JS Taro'
const num = 10000
const output = myTag`Hello ${person}. You are the ${num}th visitor`;
console.log(output);
上記はテンプレートリテラル内の値と、プレースホルダの値をそれぞれオブジェクトに設定して、戻り値として返しています。
実行結果は以下のようになります。
生の文字列の取得-rawプロパティ
以下のような仮のフォルダパスを出力したいとします。
C:\User\test\book\name
console.log('C:\User\test\book\name');
上記のようにそのまま出力しようとしても、結果はうまくいきません。
これは、\
(バックスラッシュ)と英字の組み合わせで特殊文字となる場合があるためです。
これをエスケープシーケンスといい、以下はその一例です。
エスケープシーケンス | 意味 |
---|---|
\t | 水平タブ |
\b | バックスペース |
\n | 改行 |
このエスケープシーケンスを処理する前の、生の文字列(加工前の文字列)を取得できるのが、raw
プロパティです。
function myTag(strings, ...values) {
console.log(strings[0]);
console.log(strings.raw[0]);
}
myTag`C:\User\test\book\name`;
今回は引数で受け取った値を出力するだけの、値を返さないタグ関数を用意しています。
1行目が今まで使用してきたstrings
、そして2行目がstrings
に用意されているraw
プロパティです。
raw
プロパティもstrings
と同じようにプレースホルダごとに区切られ、配列として設定されます。
2行目の出力が、加工されずそのままの文字列で出力されていることが確認できます。
生の文字列の取得-String.raw
タグ関数内でraw
プロパティを使用して、生の文字列を取得する方法を確認しました。
しかし、さきほどの方法ではプレースホルダを利用できません。
strings
やraw
には、プレースホルダを除いた値が配列として設定されるためです。
そのときに使えるのがString
に用意されているraw
関数です。
const str = 'myFolder'
const path = String.raw`C:\User\test\book_${str}\name`;
console.log(path);
String.raw
をタグ関数として利用します。
エスケープシーケンスが含まれる文字列に、プレースホルダを設定したものをテンプレートリテラルとして指定しています。
出力結果は以下のようになります。
エスケープシーケンスが加工されずそのまま出力されていますが、プレースホルダは置換されているのが確認できます。
どちらかというとraw
プロパティよりもString.raw
関数のほうが利用頻度は高そうです。
まとめ
テンプレートリテラルの基本と発展について確認しました。
テンプレートリテラルとは値をバッククォートで囲んだもので、リテラルを表すための記載形式でした。
${}
で囲んだプレースホルダを使って、テンプレートリテラル内に変数や式を埋め込むこともできます。
また、タグ付きテンプレートを使ってテンプレートリテラルに指定した値に対して任意の処理を行う事ができました。
では「はじめに」で確認した、わたしが理解できなかったコードを見てみましょう。
const MyComp = styled.div`
width: 200px;
margin: 5px;
border: 1px solid blue;
`
Styled Componentsの詳細な処理まではわかりませんが、styled.div
がタグ関数として利用されており、テンプレートリテラル内の文字列がタグ関数の引数として設定されるだろうことが想定できます。
そして、何らかの処理が行われた結果の戻り値がMyComp
に設定されるのです。
(ちなみに、MyComp
にはオブジェクトが設定されるので、styled.div
内部ではオブジェクト生成の処理が行われているようです。)
一見するとよくわからないコードも、きちんと基礎から順に追って確認することでその意味を理解することができます。
今後も、基礎からしっかり固める学習を進めていきたいと実感しました。