JS.next

JavaScriptの最新実装情報を追うブログ

テンプレートリテラルが実装された

概要

テンプレート文字列を記述できる構文が実装された。


テンプレートリテラル

基本の使い方

バッククオート『 ` 』で囲んだ文字が、文字列リテラル同様に文字列として評価される。

var t = `テンプレート`

var s = "テンプレート"

console.log(t)       // "テンプレート"
console.log(t == s)  // true


改行がそのまま認識される。

var t = `テンプ
レート`

var s = "テンプ\n"+
"レート"

console.log(t)       // "テンプ⏎レート\t"
console.log(t == s)  // true


文字列中に変数などを埋め込む

リテラル中の『${』と『}』で囲まれた部分は式として評価される。

var n = 123, f = () => 339

var t = `0${ n }45${ f() * 2 }9`

var s = "0" + n + "45" + (f() * 2) + "9"

console.log(t)       // "0123456789"
console.log(t == s)  // true


タグ付きテンプレートリテラル

基本の使い方

テンプレートリテラルの前に関数名(タグ)を記すことで、リテラルの中身を引数として関数を呼び出すことができる。
第1引数に『${~}』部分をくり抜いた文字列の配列がわたされ、第2引数には1つ目の『${~}』部分の評価値、第3引数には2つ目の『${~}』部分の評価値……以降『${~}』部分の評価値が個数分渡される。
そして関数が返した値がリテラルの評価値となる。

function fn() {
  return arguments
}

var t = fn`a${1}b${2}c${3}`

console.log(t)  // ["a", "b", "c", ""], 1, 2, 3

リテラルが『${~}』で始まったり終わったりする場合も、配列の最初や最後には空文字が入る。
よって第一引数の配列の個数は必ず、第二引数以降の個数+1になる。


生の値を使う

タグ関数の第一引数に渡される配列の各文字列は、エスケープ記号などが考慮された値になっている。
そうではなく書かれているままの生の文字列を参照したい場合は、第一引数の配列の「raw」プロパティに入っている配列を使う。

var t1 = (s => s[0])`\t\n\\`

console.log( t1 == "\t\n\\" )  // true


var t2 = (s => s.raw[0])`\t\n\\`

console.log( t2 == "\\t\\n\\\\" )  // true


String.rawと、タグ付きテンプレートリテラルの応用

「String.raw」関数は、タグ関数として使われた場合に、書かれたままの文字列を返す。
つまり

function String.raw(s, a, b, c, ......) {
  return s.raw[0] + a + s.raw[1] + b + s.raw[2] + c + ...... 
}

という処理である。 これはタグ付きテンプレートリテラル内で使うと便利である。

例えば、HTMLタグをエスケープするタグ関数safehtmlは、

function safehtml() {
  var raw = String.raw.apply(null, arguments)
  var safe = raw.replace(/</g, '&lt;').replace(......
  return safe
}

のように作り、

<div id=hello></div>
<script>
  var name = prompt('あなたの名前は?', '')
  window.hello.innerHTML = safehtml`ようこそ!${name}さん`
</script>

のように使える。


その他の特徴・注意

特徴

タグ関数の第一引数に渡される配列(CallSiteオブジェクト)はキャッシュされる。

var callSites = []

function tag(siteObj) {
  callSites.push(siteObj)
}

tag`abc${ 1 }def`
tag`abc${ 2 }def`

console.log(callSites[0] === callSites[1])  // true


注意

テンプレートリテラルでは途中で改行することもできるが、インデントなども値に含まれてしまうことに注意しないといけない。
例:

////
  ////
    ////
      var t = `
        テンプレート
      `
    ////
  ////
////

このように書くと、無駄なスペースや改行までも値に含まれてしまう。
そういう時は以下のようにそれらを取り除くタグ関数を作ると良い。

function trim() {
  var raw = String.raw.apply(null, arguments)
  return raw.split('\n').map(s => s.trim()).join('\n').replace(/(^\n)|(\n$)/g, '')
}


実装されるバージョン

V8 3.31.6(ひと通りの機能) 3.31.12(String.raw) 3.31.16(CallSiteオブジェクトのキャッシュ) [3.31.26-](マルチバイト文字でのbug fix) [3.31.66](デフォルト有効)
Chrome 41(デフォルト有効)
V8 v31以降で新機能を試すには個別にフラグを付けて起動する必要があります。