テンプレートリテラル (テンプレート文字列)

テンプレートリテラルは、組み込み式を扱うことができる文字列リテラルです。複数行の文字列や文字列挿入機能を使用することができます。

ES2015 仕様書よりも前の版では、"template strings" と呼ばれていました。

構文

`string text`

`string text line 1
 string text line 2`

`string text ${expression} string text`

tag`string text ${expression} string text`

解説

テンプレートリテラルは、ダブルクオートやシングルクオートの代わりにバックティック文字 (` `) (グレイヴ・アクセント) で囲みます。

テンプレートリテラルにはプレースホルダーを含めることができます。プレースホルダーはドル記号と波括弧 (${expression}) で示されます。プレースホルダー内の式とバックティック文字 (` `) の間にあるテキストが関数に渡されます。

既定の関数はこれらの部品を 1 つの文字列として繋げるだけです。テンプレートリテラルの前に式 (ここでは tag) がある場合、テンプレートリテラルは タグ付きテンプレート と呼ばれます。この場合、タグ式 (通常は関数) は、テンプレートリテラルと一緒に呼び出され、出力の前に実行できます。

テンプレートリテラル内でバックティック文字をエスケープするには、バックティック文字の前にバックスラッシュ (\) を置きます。

`\`` === '`' // --> true

複数行の文字列

ソースの中に挿入された改行文字は、すべてテンプレートリテラルの一部になります。

通常の文字列を使う場合は、複数行の文字列を取得するために次のような構文を使用する必要があります。

console.log('string text line 1\n' +
'string text line 2');
// "string text line 1
// string text line 2"

テンプレートリテラルを使用すれば、同じものを次のように書くことができます。

console.log(`string text line 1
string text line 2`);
// "string text line 1
// string text line 2"

式の挿入

通常の文字列に式を埋め込むには、以下のような構文を使用していると思います。

let a = 5;
let b = 10;
console.log('Fifteen is ' + (a + b) + ' and\nnot ' + (2 * a + b) + '.');
// "Fifteen is 15 and
// not 20."

テンプレートリテラルを使用すると、糖衣構文を使用して、このようにもっと読みやすく表記することができます。

let a = 5;
let b = 10;
console.log(`Fifteen is ${a + b} and
not ${2 * a + b}.`);
// "Fifteen is 15 and
// not 20."

入れ子のテンプレート

場合によっては、テンプレートを入れ子にすると、構成可能な文字列を得るのにもっとも簡単に (かつ、おそらくより読みやすく) なることがあります。バックティックをつけたテンプレートでは、テンプレート内にあるプレイスホルダー ${ } の内部で内部のバックティックを使用することができます。

例えば、条件式が true の場合、このテンプレートリテラルを return します。

ES5 の場合:

let classes = 'header';
classes += (isLargeScreen() ?
  '' : item.isCollapsed ?
    ' icon-expander' : ' icon-collapser');

ES2015 で入れ子なしのテンプレートリテラルの場合:

const classes = `header ${ isLargeScreen() ? '' :
  (item.isCollapsed ? 'icon-expander' : 'icon-collapser') }`;

ES2015 で入れ子にしたテンプレートリテラルの場合:

const classes = `header ${ isLargeScreen() ? '' :
  `icon-${item.isCollapsed ? 'expander' : 'collapser'}` }`;

タグ付きテンプレート

タグ付きテンプレートは、テンプレートリテラルのより高度な形式です。

タグを使用すると、テンプレートリテラルを関数で解析できます。タグ関数の最初の引数には、文字列リテラルの配列を含みます。残りの引数は式に関連付けられます。

タグ関数は、これらの引数に対して何でも望み通りの操作を実行することができ、加工された文字列を返します。 (または、以下の例の一つで示しているように、まったく異なるものを返すこともできます。)

タグに使用される関数の名前は、自由に指定できます。

let person = 'Mike';
let age = 28;

function myTag(strings, personExp, ageExp) {
  let str0 = strings[0]; // "That "
  let str1 = strings[1]; // " is a "

  // There is technically a string after
  // the final expression (in our example),
  // but it is empty (""), so disregard.
  // let str2 = strings[2];

  let ageStr;
  if (ageExp > 99){
    ageStr = 'centenarian';
  } else {
    ageStr = 'youngster';
  }

  // テンプレートリテラルを用いて組み立てた文字列を返すこともできます
  return `${str0}${personExp}${str1}${ageStr}`;
}

let output = myTag`That ${ person } is a ${ age }`;

console.log(output);
// That Mike is a youngster

タグ関数は文字列を返さなくても構いません。

function template(strings, ...keys) {
  return (function(...values) {
    let dict = values[values.length - 1] || {};
    let result = [strings[0]];
    keys.forEach(function(key, i) {
      let value = Number.isInteger(key) ? values[key] : dict[key];
      result.push(value, strings[i + 1]);
    });
    return result.join('');
  });
}

let t1Closure = template`${0}${1}${0}!`;
//let t1Closure = template(["","","","!"],0,1,0);
t1Closure('Y', 'A');                      // "YAY!"

let t2Closure = template`${0} ${'foo'}!`;
//let t2Closure = template(["","","!"],0,"foo");
t2Closure('Hello', {foo: 'World'}); // "Hello World!"

let t3Closure = template`I'm ${'name'}. I'm almost ${'age'} years old.`;
//let t3Closure = template(["I'm ", ". I'm almost ", " years old."], "name", "age");
t3Closure('foo', {name: 'MDN', age: 30}); //"I'm MDN. I'm almost 30 years old."
t3Closure({name: 'MDN', age: 30}); //"I'm MDN. I'm almost 30 years old."

加工前の文字列

タグ関数に渡される第 1 引数では、特別な raw プロパティが利用できます。このプロパティを通して、エスケープシーケンスを処理する前の、入力された通りの加工前の文字列を参照することができます。

function tag(strings) {
  console.log(strings.raw[0]);
}

tag`string text line 1 \n string text line 2`;
// logs "string text line 1 \n string text line 2" ,
// including the two characters '\' and 'n'

加えて、 String.raw() メソッドがあり、既定のテンプレート関数のように文字列を連結した形で加工前の文字列を生成することができます。

let str = String.raw`Hi\n${2+3}!`;
// "Hi\n5!"

str.length;
// 6

Array.from(str).join(',');
// "H,i,\,n,5,!"

タグ付きテンプレートとエスケープシーケンス

ES2016 での動作

ECMAScript 2016 時点では、タグ付きテンプレートの以下のエスケープシーケンスには、次のようなルールが適用されます。

  • Unicode のエスケープシーケンスは "\u" で始まること。例: \u00A9
  • Unicode のコードポイントのエスケープは "\u{}" で示すこと。例: \u{2F804}
  • 16 進数のエスケープは "\x" で始まること。例: \xA9
  • 8 進数のリテラルのエスケープは "\0o" で始まり、その後数字が続くこと。例: \0o251

このルールのもとでは、下に示す例のようなタグ付きテンプレートが問題となります。なぜなら、 ECMAScript の文法に従ってこのテキストを解釈しようとすると、パーサーは Unicode の有効のエスケープシーケンスを探そうとするも、不正な構文が検出されてしまうからです。

latex`\unicode`
// 古い ECMAScript バージョン (ES2016 以前) では、以下のような例外が投げられる
// SyntaxError: malformed Unicode character escape sequence

ES2018 revision での不正なエスケープシーケンス

タグ付きテンプレートでは、他のエスケープシーケンスが一般的な言語 (DSLLaTeX など) の埋め込みも許容する必要があります。 ECMAScript プロポーザル Template Literal Revision (Stage 4, to be integrated in the ECMAScript 2018 standard) では、タグ付きテンプレートから、ECMAScript のエスケープシーケンスについての構文的な制約が除外されています。

しかし、埋め込んで「加工された」結果の中においても、不正なエスケープシーケンスがあれば、それを提示する必要があります。こうした不正なエスケープシーケンスは、「加工された」の配列の中では、undefined の要素として表示されます。

function latex(str) {
  return { "cooked": str[0], "raw": str.raw[0] }
}

latex`\unicode`

// { cooked: undefined, raw: "\\unicode" }

このエスケープシーケンスの制約は、タグ付きテンプレートのみが除外となります。タグ付きでないテンプレートリテラルの場合は、除外対象ではありません:

let bad = `bad escape sequence: \unicode`;

仕様書

ブラウザーの互換性

BCD tables only load in the browser

関連情報