6
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptのテンプレートリテラルを完璧に理解する

Last updated at Posted at 2024-02-25

はじめに

今回はJavaScriptにおけるテンプレートリテラルについて、基本構造から発展的な内容までを確認していく記事です。

話は変わりますが、React(JavaScriptのライブラリ)の勉強をするなかで、Styled Component というスタイルを定義するためのライブラリを知りました。
その記載例が以下です。

Styled Componentsの記載例
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}`);

実行結果は以下の通りです。
image.png

変数や式を埋め込むためには、テンプレートリテラル内で使いたい式や変数を${}で囲みます。
これをプレースホルダといいます。

ちなみに、テンプレートリテラルを使わずに、シングルクォート(通常の文字列)で同じように表現すると以下のようになります。

テンプレートリテラルなし
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);

上記はテンプレートリテラル内の値と、プレースホルダの値をそれぞれオブジェクトに設定して、戻り値として返しています。

実行結果は以下のようになります。

image.png

生の文字列の取得-rawプロパティ

以下のような仮のフォルダパスを出力したいとします。
C:\User\test\book\name

console.log('C:\User\test\book\name');

上記のようにそのまま出力しようとしても、結果はうまくいきません。
image.png

これは、\(バックスラッシュ)と英字の組み合わせで特殊文字となる場合があるためです。
これをエスケープシーケンスといい、以下はその一例です。

エスケープシーケンス 意味
\t 水平タブ
\b バックスペース
\n 改行

このエスケープシーケンスを処理する前の、生の文字列(加工前の文字列)を取得できるのが、rawプロパティです。

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と同じようにプレースホルダごとに区切られ、配列として設定されます。

出力結果は以下のようになります。
image.png

2行目の出力が、加工されずそのままの文字列で出力されていることが確認できます。

生の文字列の取得-String.raw

タグ関数内でrawプロパティを使用して、生の文字列を取得する方法を確認しました。
しかし、さきほどの方法ではプレースホルダを利用できません。
stringsrawには、プレースホルダを除いた値が配列として設定されるためです。
そのときに使えるのがStringに用意されているraw関数です。

String.rawの使用例
const str = 'myFolder'
const path = String.raw`C:\User\test\book_${str}\name`;
console.log(path);

String.rawをタグ関数として利用します。
エスケープシーケンスが含まれる文字列に、プレースホルダを設定したものをテンプレートリテラルとして指定しています。

出力結果は以下のようになります。
image.png
エスケープシーケンスが加工されずそのまま出力されていますが、プレースホルダは置換されているのが確認できます。

どちらかというとrawプロパティよりもString.raw関数のほうが利用頻度は高そうです。

まとめ

テンプレートリテラルの基本と発展について確認しました。

テンプレートリテラルとは値をバッククォートで囲んだもので、リテラルを表すための記載形式でした。
${}で囲んだプレースホルダを使って、テンプレートリテラル内に変数や式を埋め込むこともできます。

また、タグ付きテンプレートを使ってテンプレートリテラルに指定した値に対して任意の処理を行う事ができました。

では「はじめに」で確認した、わたしが理解できなかったコードを見てみましょう。

Styled Componentsの記載例(再掲)
const MyComp = styled.div`
  width: 200px;
  margin: 5px;
  border: 1px solid blue;
`

Styled Componentsの詳細な処理まではわかりませんが、styled.divがタグ関数として利用されており、テンプレートリテラル内の文字列がタグ関数の引数として設定されるだろうことが想定できます。
そして、何らかの処理が行われた結果の戻り値がMyCompに設定されるのです。
(ちなみに、MyComp にはオブジェクトが設定されるので、styled.div内部ではオブジェクト生成の処理が行われているようです。)

一見するとよくわからないコードも、きちんと基礎から順に追って確認することでその意味を理解することができます。
今後も、基礎からしっかり固める学習を進めていきたいと実感しました。

参考にしたサイト

6
10
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?