LoginSignup
875

More than 5 years have passed since last update.

JavaScript の テンプレートリテラル を極める!

Last updated at Posted at 2015-08-07

テンプレートリテラルとは、ECMAScript 6 で新しく使えるようになった構文のひとつです。
言わばヒアドキュメントのようなものです。めちゃくちゃ便利です!
2015年8月現在、Google Chrome と Firefox の最新版では既に使えるようになっています。
2016年9月現在、Edgeでも使えるようになっており、Google Chrome や Firefox も含めたほとんどのモダンブラウザで利用可能です。
(残念ながらモバイルブラウザではまだ非対応のこともあるので、要注意です。)

基本

この内容を知っているだけでもカナリ使えます!

バッククオート(`~`)で囲む!

var a = "ジャバスクリプト";
var b = `ジャバスクリプト`;
console.log( a === b ); // true
console.log( b ); // ジャバスクリプト

つまり、シングルクオート('text') や ダブルクオート("text") と基本は同じです!

改行をそのまま書ける!

var a = "ジャバ\nスクリプト";
var b = `ジャバ
スクリプト`;
console.log( a === b ); // true
console.log( b ); // ジャバ⏎スクリプト

"\n" の代わりに、`⏎` と書くことができるということです。

もちろん、今までのエスケープも併せて使える!

var a = "ジャバ\nスク\nリプト";
var b = `ジャバ\nスク
リプト`;
console.log( a === b ); // true
console.log( b ); // ジャバ⏎スク⏎リプト
var a = "ジャバ\\スク\nリプト";
var b = `ジャバ\\スク
リプト`;
console.log( a === b ); // true
console.log( b ); // ジャバ\スク⏎リプト

`\n` と書くことも、`⏎` と書くこともできるということです。

エスケープせず、そのまま文字にしたいならString.raw

`~`の中では\nみたいなエスケープが可能ですが、場合によっては余計なお世話に感じることもあります。
エスケープせず本当にそのままの文字が欲しい場合は、String.raw`~` を使います。

var a = "ジャバ\\スク\\nリプト";
var b = String.raw`ジャバ\スク\nリプト`;
console.log( a === b ); // true
console.log( b ); // ジャバ\スク\nリプト

つまり、"\\n" のようにエスケープする必要があるところを、String.raw`\n` のようにそのまま書けるということです!

`${~}` の中には、変数や計算式を入れることができる!

var foo = "--";
var a = "ジャバ" + foo + "スク" + (111+222) + "リプト";
var b = `ジャバ${foo}スク${111+222}リプト`;
console.log( a === b ); // true
console.log( b ); // ジャバ--スク333リプト

つまり、+演算子 を使わずに文字列を繋げていくことができます。
`${~}` の中には、式であれば何でも入れることができます!

でも ${} を文字として入れたいときもある。

そんな時は、`\${}``$\{}` のようにエスケープします。

var a = "ジャバ${foo}スクリプト";
var b = `ジャバ\${foo}スクリプト`;
console.log( a === b ); // true
console.log( b ); // ジャバ${foo}スクリプト

もちろん、String.raw`~``${~}` を両方使うことも可能!

var foo = "--";
var a = "ジャバ" + foo + "スク\\リプト";
var b = String.raw`ジャバ${foo}スク\リプト`;
console.log( a === b ); // true
console.log( b ); // ジャバ--スク\リプト

発展

この内容を知っていればもっと便利!
基本はバッチリという人はぜひこっちもマスターしてください!

String.raw`~` の正体

String.raw はただの関数です

func() は関数を実行する構文なのと同様に、
func`~` もまた、関数を実行する構文なのです。
(この構文は、タグ付きテンプレートリテラル(Tagged Templates)と言います。)

ただし、func`~` のように関数を実行した場合、渡される引数がちょっと変わっています
どんな引数が渡されるのか、以下を例にとってみてみましょう。

function tag(){
  console.log(arguments);
}
tag`\\ジャバ${true}スク${1+2}リプト`;
/*
  0: ["\ジャバ", "スク", "リプト", raw: ["\\ジャバ", "スク", "リプト"]]
  1: true (Boolean型)
  2: 3 (Number型)
*/

第1引数には文字列の配列が入っています。リテラル中に ${~} が含まれる場合、${~} の前後で分割されます
さらにその配列の rawプロパティ には、エスケープされていない文字列の配列が入っています。
第2引数以降には ${~} の中身で評価された式の値が順に入っています。(String型に変換されていない点に注意です。)

つまり、String.raw を自前で定義すると以下のようになります。

String.raw = function(strings){
    var str = "";
    var raws = strings.raw;
    var subs = Array.prototype.slice.call(arguments, 1);
    for(var i=0;i<raws.length;i++){
        str += raws[i];
        if(i < raws.length - 1) str += subs[i];
    }
    return str;
}; 
String.raw = ({raw:raws}, ...subs) => {
    let str = "";
    for(let s of raws) str += s + (subs.length ? subs.shift() : "");
    return str;
}; 

自分で定義した関数でタグ付きテンプレートリテラルを使ってみる

HTML のエスケープとかに使えます。

function tagHtmlEscape(strings){
    var str = "";
    var subs = Array.prototype.slice.call(arguments, 1);
    for(var i=0;i<strings.length;i++){
        str += strings[i];
        if(i < strings.length - 1) str += subs[i].replace(/&/g, "&amp;").replace(/</g, "&lt;");
    }
    return str;
}; 
var userinput = '<script>alert("XSSテスト");</script>';
var message = tagHtmlEscape`<p>${userinput}</p>`;
console.log(message); // "<p>&lt;script>alert("XSSテスト");&lt;/script></p>"

マニアック

String.raw`~` の弱点

エスケープせずそのままの文字を表現できるという魅力がありますが、全ての文字を表現できるわけではありません。
例えば、文字列の最後が \ で終わるようにはできません。

エラー!
var b = String.raw`ジャバ\スク\\リプト\`;

これは、`ジャバ\スク\\リプト\` をテンプレートリテラルと見なした際、終わりの \` がエスケープされているとみなされ、テンプレートリテラルが終わらないからです。

以下のように書けば解決しますが、あまりスマートではありませんね…。

var b = String.raw`ジャバ\スク\\リプト` + "\\";

ES6 のテンプレートリテラル、改善の余地があると感じます。

タグ付きテンプレートリテラルで渡される第一引数の性質

タグ付きテンプレートリテラル(func`~`)で渡される第一引数には、以下の変わった性質があります。
1. プロパティの追加・変更・削除が不可能
2. rawの文字列の配列の要素がすべて一致する場合、何度実行しても同じ配列オブジェクトを参照する

以上の性質を示すコードが以下になります。

function tag(ary){ return ary; }
var a = tag`ジャバ${123}スクリプト`;
var b = tag`ジャバ${456}スクリプト`;
console.log(a); // ["ジャバ", "スクリプト", raw: ["ジャバ", "スクリプト"]]
a[0] = "JAVA"; // 変更不可
delete a[1]; // 削除不可
a[2] = ""; // 追加不可
console.log(a); // ["ジャバ", "スクリプト", raw: ["ジャバ", "スクリプト"]]
console.log(a === b); // 同じオブジェクトを指している

改行コードは気にしなくていい

改行コードには、CR、LF、CR+LF の3種類があります。
それでは、`~` の中に改行を入れた時、改行コードを意識しないといけないのかというと、その必要はありません。
`~` の中の改行は自動的に全て LF に統一されます

var a = eval("`ジャバ\rスクリプト`");
var b = eval("`ジャバ\nスクリプト`");
var c = eval("`ジャバ\r\nスクリプト`");
var s = "ジャバ\nスクリプト";
console.log(a === s, b === s, c === s); // true, true, true

もっと細かな話をすると、実は HTML 内に書いた改行コードは全てパースする時点で LF に統一されるのですが…。これはまた別の機会に。

ショートコーディング

タグ付きテンプレートリテラルの性質を使えば、面白いショートコーディングができます。

console.log( "abcde".split`` ); // .split("") より2文字短い
console.log( [1,2,3].join`` ); // .join("") より2文字短い
console.log( "aabbcc".split`b`.join`B` ); // .replace(/b/g,"B") より 1文字短い
console.log( "abcde".match`b.d` ); // .match(/b.d/) より2文字短い
console.log( [1,2,3].push`` ); // .length+1 より 2文字短い

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
875