ES6 テンプレート文字列によるリテラルの取得

Addy Osmani 氏
Addy Osmani 氏

これまで JavaScript の文字列は限定的で、Python や Ruby といった言語で使われる可能性のある機能は欠けていました。ES6 のテンプレート文字列(Chrome 41 以降で利用可能)は根本的に変更します。ドメイン固有言語(DSL)で文字列を定義する方法が導入されました。これにより、次のようなメリットが得られます。

  • 文字列補間
  • 埋め込み式
  • マルチライン文字列(ハックなし)
  • 文字列形式
  • 安全な HTML エスケープ、ローカライズなどのための文字列のタグ付け

テンプレート文字列は、現在おなじみのとおり、文字列に別の機能を詰め込むのではなく、こうした問題を解決するまったく異なる方法を導入します。

構文

テンプレート文字列には、通常の文字列で使用される一重引用符や二重引用符ではなく、バッククォート(``)を使用します。したがって、テンプレート文字列は次のように記述できます。

var greeting = `Yo World!`;

これまでのところ、テンプレート文字列には通常の文字列以上のものはありません。これを変更しましょう。

文字列置換

その最初の大きなメリットのひとつは、文字列置換です。置換を使用すると、任意の有効な JavaScript 式(変数の追加など)を取ることができ、テンプレート リテラルの中では、結果は同じ文字列の一部として出力されます。

テンプレート文字列には、次に示すように、${ } 構文を使用して文字列置換用のプレースホルダを含めることができます。

// Simple string substitution
var name = "Brendan";
console.log(`Yo, ${name}!`);

// => "Yo, Brendan!"

テンプレート文字列の文字列置換はすべて JavaScript の式であるため、変数名以外にもさまざまな形式で置換できます。たとえば以下の例では、式補間を使用して、読み取り可能なインライン計算を埋め込むことができます。

var a = 10;
var b = 10;
console.log(`JavaScript first appeared ${a+b} years ago. Wow!`);

//=> JavaScript first appeared 20 years ago. Wow!

console.log(`The number of JS MVC frameworks is ${2 * (a + b)} and not ${10 * (a + b)}.`);
//=> The number of JS frameworks is 40 and not 200.

また、式内の関数でも非常に便利です。

function fn() { return "I am a result. Rarr"; }
console.log(`foo ${fn()} bar`);
//=> foo I am a result. Rarr bar.

${} は、メンバー式やメソッド呼び出しなど、あらゆる種類の式で適切に機能します。

var user = {name: 'Caitlin Potter'};
console.log(`Thanks for getting this into V8, ${user.name.toUpperCase()}.`);

// => "Thanks for getting this into V8, CAITLIN POTTER";

// And another example
var thing = 'template strings';
console.log(`Say hello to ${thing}.`);

// => Say hello to template strings

文字列内でバッククォートが必要な場合は、次のようにバックスラッシュ文字 \ を使用してエスケープできます。

var greeting = `\`Yo\` World!`;

複数行文字列

JavaScript の複数行文字列には、かなり前から、ちょっとした回避策が必要でした。現在の解決策では、文字列を 1 行に配置するか、各改行の前に \(バックスラッシュ)を使用して複数行の文字列に分割する必要があります。例:

var greeting = "Yo \
World";

最近のほとんどの JavaScript エンジンでは問題なく動作するはずですが、動作自体はまだややハックとなっています。文字列連結を使用して複数行のサポートを装うこともできますが、同様に必要とするものが残っています。

var greeting = "Yo " +
"World";

テンプレート文字列を使用すると、複数行の文字列を大幅に簡素化できます。必要な場所に改行を追加して、BOOM を追加するだけです。次の例をご覧ください。

バッククォート構文内の空白文字も文字列の一部と見なされます。

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

タグ付きテンプレート

ここまで、テンプレート文字列を文字列の置換と複数行文字列の作成に使用する方法を見てきました。もう一つの優れた機能は、タグ付きテンプレートです。タグ付きテンプレートでは、テンプレート文字列の前に関数名を追加してテンプレート文字列を変換します。例:

fn`Hello ${you}! You're looking ${adjective} today!`

タグ付けされたテンプレート文字列のセマンティクスは、通常のものとは大きく異なります。基本的に、これらは特殊なタイプの関数呼び出しであり、上記の「デザート」に

fn(["Hello ", "! You're looking ", " today!"], you, adjective);

(n + 1) 番目の引数は、文字列配列の n 番目と (n + 1) 番目のエントリの間で行われる置換に対応していることに注意してください。これはあらゆる種類のことに役立ちますが、最も簡単なものは、補間された変数を自動的にエスケープすることです。

たとえば、次のような HTML エスケープ関数を作成できます。

html`<p title="${title}">Hello ${you}!</p>`

適切な変数を置き換えた文字列で、HTML 安全でない文字をすべて置き換えた文字列を返します。やってみましょう。HTML エスケープ関数は、ユーザー名とコメントの 2 つの引数を取ります。どちらにも、HTML に安全でない文字('、"、<、>、&)が含まれている可能性があります。たとえば、ユーザー名が「Domenic Denicola」で、コメントが「& is a fun tag」の場合は、次のように出力する必要があります。

<b>Domenic Denicola says:</b> "&amp; is a fun tag"

したがって、タグ付けされたテンプレート ソリューションは次のように記述できます。

// HTML Escape helper utility
var util = (function () {
    // Thanks to Andrea Giammarchi
    var
    reEscape = /[&<>'"]/g,
    reUnescape = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g,
    oEscape = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        "'": '&#39;',
        '"': '&quot;'
    },
    oUnescape = {
        '&amp;': '&',
        '&#38;': '&',
        '&lt;': '<',
        '&#60;': '<',
        '&gt;': '>',
        '&#62;': '>',
        '&apos;': "'",
        '&#39;': "'",
        '&quot;': '"',
        '&#34;': '"'
    },
    fnEscape = function (m) {
        return oEscape[m];
    },
    fnUnescape = function (m) {
        return oUnescape[m];
    },
    replace = String.prototype.replace
    ;
    return (Object.freeze || Object)({
    escape: function escape(s) {
        return replace.call(s, reEscape, fnEscape);
    },
    unescape: function unescape(s) {
        return replace.call(s, reUnescape, fnUnescape);
    }
    });
}());

// Tagged template function
function html(pieces) {
    var result = pieces[0];
    var substitutions = [].slice.call(arguments, 1);
    for (var i = 0; i < substitutions.length; ++i) {
        result += util.escape(substitutions[i]) + pieces[i + 1];
    }

    return result;
}

var username = "Domenic Denicola";
var tag = "& is a fun tag";
console.log(html`<b>${username} says</b>: "${tag}"`);
//=> <b>Domenic Denicola says</b>: "&amp; is a fun tag"

他にも、自動エスケープ、書式設定、ローカライズ、一般的にはより複雑な置換などの用途が考えられます。

// Contextual auto-escaping
qsa`.${className}`;
safehtml`<a href="${url}?q=${query}" onclick="alert('${message}')" style="color: ${color}">${message}</a>`;

// Localization and formatting
l10n`Hello ${name}; you are visitor number ${visitor}:n! You have ${money}:c in your account!`

// Embedded HTML/XML
jsx`<a href="${url}">${text}</a>` // becomes React.DOM.a({ href: url }, text)

// DSLs for code execution
var childProcess = sh`ps ax | grep ${pid}`;

概要

テンプレート文字列は、Chrome 41 ベータ版以降、IE Tech Preview、Firefox 35 以降、io.js で使用できます。現在本番環境で使用する場合、実質的には、Traceur や 6to5 などの主要な ES6 トランスパイラでサポートされています。実際に試してみたい場合は、Chrome サンプル リポジトリにあるテンプレート文字列のサンプルをご覧ください。また、ES5 の ES6 Equivalents も参考になります。ここでは、現在 ES5 を使用して提供する糖化テンプレート文字列のいくつかを実現する方法について説明しています。

テンプレート文字列は、JavaScript に多くの重要な機能をもたらします。たとえば、文字列や式を補間する方法、複数行文字列、独自の DSL を作成する機能などです。

その中でも特に重要な機能として、タグ付けされたテンプレートがあります。これは、このような DSL を作成するための重要な機能です。テンプレート文字列の一部を引数として受け取り、文字列と置換を使用して文字列の最終出力を決定する方法を決定できます。

関連情報