memolu

いろいろメモってます

ES6 - Template stringsを解説してみる

本連載について

本連載では少しづつES6について記載していこうと思います。 第七回はTemplate stringsを学びます。 発表されてから久しいですが、まだ習得されていない方は今からでも勉強してみてはいかがでしょうか。

Template strings

Template stringsは文字列の新しい扱い方になります。具体的には文字列の中に計算式や評価式を含めることが可能で、その結果を一つの文字列として扱うことができる機能です。""''のような文字列リテラルに追加される形で実装されました。Template stringsは````(バッククオート)のリテラルを使用します。

// 文字列リテラルなので問題なく動作する
var hello = `Hello!!`;
console.log(hello); // Hello!!

ブラウザサポート

ブラウザサポートについては以下をご確認ください。

http://kangax.github.io/compat-table/es6/

Template stringsの使い方

まずは使い方を説明します。
次のコードはTemplate stringsを使用せずに記述した文字列連結を行う処理の一例です。文字列連結の部分では、宣言した変数内の文字列や数字を+演算子を使用して連結しています。

var name = 'ymmt';
var age = 31;

var greet = 'わたしの名前は' + name + 'です。年齢は' + age + '歳です。';
console.log(greet); //  わたしの名前はymmtです。年齢は31歳です。'

これでも処理自体は問題ないですが、変数greetので+演算子を2回使用したりとやや冗長的になります。
これをTemplate stringsを使用して置き換えてみたものが下のコードです。

var name = 'ymmt';
var age = 31;

var greet = `わたしの名前は${name}です。年齢は${age}歳です。`;
console.log(greet); //  わたしの名前はymmtです。年齢は31歳です。'

改行

javascriptの文字列で改行を扱うのは少し工夫が必要です。

var hello = 'hello';

// +演算子で連結
var code = '<div>\n' +
                '<p>' + hello + '</p>\n'
              '</div>';

// 配列のjoinメソッドで\nを追加
var code2 = [
    '<div>',
    '<p>' + hello + '</p>',
    '</div>'
].join('\n');

これもTemplate stringsで置き換えてみます。

var hello = 'hello';

var code = `<div>
    <p>${hello}</p>
</div>
`;

このように簡潔に記載することができるようになります。

Template stringsの'${}'中では評価式を記述することが可能で、評価式の処理結果がそのまま出力されます。

var str = `今月は${(new Date).getMonth() + 1}月です`;
console.log(str); // 今月は7月です

今までは(new Date).getMonth() + 1の部分は別で変数などに代入し、+演算子などを使用して文字列連結を行っていました。または、Handlebars.jsなどの代表的なテンプレートエンジンを使用して実装してきました。
今回のTemplate stringsにより、このような外部ツールに頼ることなく言語仕様として表現することができるようになりました。

タグ

Template stringsにはバッククオートで囲った文字列の前に関数を呼ぶことができます。この関数はタグ関数と呼ばれ、${}内の文字列を受け取り、具体的に処理することができます。
次のコードでは変数で宣言したnamtage「」をつけるようにしています。

var name = 'ymmt';
var age = 31;

function tag(strings, ...values) {
    console.log(strings); // ["わたしの名前は", "です。年齢は", "歳です。"]
    console.log(values); // ["ymmt", 31]

    var _values = values.map((value) => {
        return value = `「${value}」`;
    });

    return strings.reduce((prev, current, index) => {
        return prev + _values[index - 1] + current;
    });

    console.log(_values);
}

var greet = tag `わたしの名前は${name}です。年齢は${age}歳です。`;
console.log(greet); //  わたしの名前は「ymmt」です。年齢は「31」歳です。'

コードの説明を行います。
変数greetに記載されているtagがここで解説するタグ関数になります。tag関数は引数として、strings_valuesという値が渡っています。
stringsにはベタ打ちのテキストが配列として渡っています。["わたしの名前は", "です。年齢は", "歳です。"]の部分です。
valuesにはTemplate stringsの${}で指定したテキストが入っています。["ymmt", 31]の部分です。
タグ関数内では変数_valuesを作成しmap関数を使用して「」をそれぞれの値に追加しています。

変数greetには最終的にタグ関数の中でリターンされた値が返ります。そのため、いったん配列でバラバラになった要素をもう一度テキストにまとめなければなりません。
そのため配列のreduce関数を使用して、もう一度テキストにまとめてreturnしています。

rawプロパティ

上に記載したタグ関数の第一引数には処理するテキストの${}以外の情報が渡されていました。
しかし\nなどの改行コードは改行として処理されてしまい、\nの文字列を触ることができません。
このような生の文字列にアクセスしたい場合、第一引数のrawプロパティを参照することで解決できます。

var name = 'ymmt';
var age = 31;

function tag(strings, ...values) {
    console.log(strings); // ["わたしの名前は", "です。↵年齢は", "歳です。];
    console.log(strings.raw); // ["わたしの名前は", "です。\n年齢は", "歳です。"]
}

var greet = tag `わたしの名前は${name}です。\n年齢は${age}歳です。`;

stringsには改行として\nが含まれておりますが、strings.rawには\nが文字列として出力されていることがわかります。

String.rawメソッド

ビルトインのStringコンストラクタにはrawメソッドが追加されました。
rawメソッドはTemplate stringsを文字列として組み立てなおして返す仕様があります。 混同しやすいですが、上で説明したタグ関数の第一引数に含まれるrawプロパティとは別物です。

conosle.log(String.raw`${ 1 + 1}\n`); // 2\n

これを利用して、reduceメソッドなどを使用せずとも文字列を連結して返すことが可能です。
まずは下のコードは上でも紹介させていただいた、reduceメソッドで文字列をreturnしているコードです。

var name = 'ymmt';
var age = 31;

function tag(strings, ...values) {
    console.log(strings); // ["わたしの名前は", "です。年齢は", "歳です。"]
    console.log(values); // ["ymmt", 31]

    var _values = values.map((value) => {
        return value = `「${value}」`;
    });

    return strings.reduce((prev, current, index) => {
        return prev + _values[index - 1] + current;
    });

    console.log(_values);
}

var greet = tag `わたしの名前は${name}です。年齢は${age}歳です。`;
console.log(greet); //  わたしの名前は「ymmt」です。年齢は「31」歳です。'

これをString.rawメソッドを使用して簡潔にしたものが以下になります。

var name = 'ymmt';
var age = 31;

function tag(strings, ...values) {
    console.log(strings); // ["わたしの名前は", "です。年齢は", "歳です。"]
    console.log(values); // ["ymmt", 31]

    var _values = values.map((value) => {
        return value = `「${value}」`;
    });

    return String.raw(strings, ..._values);
}

var greet = tag `わたしの名前は${name}です。年齢は${age}歳です。`;
console.log(greet); //  わたしの名前は「ymmt」です。年齢は「31」歳です。'

return String.raw(strings, ..._values);がまさに該当の箇所になります。
String.rawメソッドの仕様は、第一引数の1つめの値、第二引数、第一引数の2つめの値、第三引数...のように組み立てられます。そのため引数に渡す値は...をつけて処理したほうが柔軟な対応ができるでしょう。  

このようにreduceメソッドを使っていた部分もString.rawメソッドを使うことで処理をよりシンプルにすることが可能です。

まとめ

このようにTemplate stringsを使うことで、文字列処理を非常に柔軟に対応することができるようになりました。
今までの改行処理をシンプルにするためなどで、テンプレートエンジンなどを活用してきたケースがありましたが、言語仕様で表現できるようになり、より簡潔でシンプルに処理をまとめられそうです。