ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript ES6のテンプレート文字列を詳しく解説_基礎知識
ES6 では、新しいタイプの文字列リテラルであるテンプレート文字列が導入されており、バックティック (`) の使用を除いて、通常の文字列と同じように見えます。最も単純なケースでは、これらは単なる普通の文字列です:
context.fillText(`Ceci n'est pas une cha?ne.`, x, y); context.fillText(`Ceci n'est pas une cha?ne.`, x, y);
は、テンプレート文字列と呼ばれます。これは、テンプレート文字列が JS に単純な文字列補間機能を導入するためです。つまり、JS 値を文字列に便利かつエレガントに挿入できます。
テンプレート文字列はさまざまな場所で使用できます。次の目立たないエラー メッセージを見てください:
function authorize(user, action) { if (!user.hasPrivilege(action)) { throw new Error( `User ${user.name} is not authorized to do ${action}.`); } } function authorize(user, action) { if (!user.hasPrivilege(action)) { throw new Error( `User ${user.name} is not authorized to do ${action}.`); } }
上記のコードでは、${user.name} と ${action} はテンプレート プレースホルダーと呼ばれ、JavaScript は user.name と action の値をそれぞれ対応する位置に挿入し、「User」を生成します。 「このように、ヨレンドルフにはホッケーをする権限がありません。」
演算子よりも洗練された構文ができたので、期待できる機能をいくつか紹介します。
テンプレートのプレースホルダーには任意の JavaScript 式を使用できるため、関数呼び出しと四則演算は正当です。 (テンプレート文字列を別のテンプレート文字列内にネストすることもできます。)
値が文字列でない場合は、文字列に変換されます。たとえば、アクションがオブジェクトの場合、そのオブジェクトの .toString() が呼び出されて文字列に変換されます。
テンプレート文字列でバッククォートを使用する場合は、バッククォートをバックスラッシュでエスケープする必要があります。
同様に、テンプレート文字列で ${ を出力する場合は、バックスラッシュを使用してエスケープする必要もあります (${ または ${)。
テンプレート文字列は複数行にまたがることができます:
$("#warning").html(` <h1>Watch out!</h1> <p>Unauthorized hockeying can result in penalties of up to ${maxPenalty} minutes.</p> `); $("#warning").html(` <h1>Watch out!</h1> <p>Unauthorized hockeying can result in penalties of up to ${maxPenalty} minutes.</p> `);
テンプレート文字列内のすべてのスペース、改行、インデントがそのまま結果文字列に出力されます。
テンプレート文字列でできないことを見てみましょう:
特殊文字は自動的にエスケープされません。クロスサイト スクリプティングの脆弱性を回避するには、通常の文字列と同様に、信頼できないデータにも注意する必要があります。
国際ライブラリでは使用できず、特殊な言語形式の数値や日付などは処理されません。
テンプレート エンジン (Mustache や Nunjucks など) の代替ではありません。テンプレート文字列にはループを処理するための構文がありません。配列からテーブルを構築することはできません。
これらの制限に対処するために、ES6 は開発者とライブラリ設計者に別のタイプのテンプレート文字列であるタグ テンプレートを提供します。
タグ テンプレートの構文は非常に簡単で、開始バッククォートの前にタグを導入するだけで済みます。最初の例: SaferHTML を見ると、このタグ テンプレートを使用して、前述の最初の制限である特殊文字の自動エスケープを解決します。
SaferHTML メソッドは ES6 標準ライブラリでは提供されていないため、自分で実装する必要があることに注意してください:
var message = SaferHTML`<p>${bonk.sender} has sent you a bonk.</p>`; var message = SaferHTML`<p>${bonk.sender} has sent you a bonk.</p>`;
ここでの SaferHTML タグは単一の識別子であり、タグは SaferHTML.escape などの属性、またはメソッド呼び出し SaferHTML.escape({unicodeControlCharacters: false}) であることもあります。正確には、任意の ES6 メンバー式または呼び出し式をタグとして使用できます。
テンプレート文字列は単なる文字列連結の糖衣構文であるのに対し、ラベル テンプレートはまったく別のもの、つまり関数呼び出しであることがわかります。
したがって、上記のコードは次と同等です:
var message = SaferHTML(templateData, bonk.sender); var message = SaferHTML(templateData, bonk.sender);
ここで、templateData はソース テンプレート文字列に基づいて JS エンジンによって生成された不変の文字列配列です。テンプレート文字列にはプレースホルダーで区切られた 2 つの文字列が含まれるため、この配列には 2 つの要素が含まれます。 : Object.freeze(["e388a4556c0f65e1904146cc1a846bee", " はあなたにひどいことを送りました。94b3e26ee717c64999d7867364b1b4a3"]
(実際、templateData には別の属性 templateData.raw があります。この記事では、この属性については詳しく説明しません。この属性の値も配列であり、タグ テンプレート内のすべての文字列部分が含まれますが、 string ES6 の組み込みタグ String.raw は、これらの文字列 )
を使用します。これにより、SaferHTML メソッドはこれら 2 つの文字列を自由に解析できるようになり、N 個の置換メソッドが存在します。
読み続けると、SaferHTML メソッドを実装する方法が疑問に思うかもしれません。
実装 (要点) は次のとおりです:
function SaferHTML(templateData) { var s = templateData[0]; for (var i = 1; i < arguments.length; i++) { var arg = String(arguments[i]); // Escape special characters in the substitution. s += arg.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">"); // Don't escape special characters in the template. s += templateData[i]; } return s; } function SaferHTML(templateData) { var s = templateData[0]; for (var i = 1; i < arguments.length; i++) { var arg = String(arguments[i]); // Escape special characters in the substitution. s += arg.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">"); // Don't escape special characters in the template. s += templateData[i]; } return s; }
有了上面的方法,即使使用一个恶意的用户名,用户也是安全的。
一个简单的例子并不足以说明标签模板的灵活性,让我们重温一下上面列举的模板字符串的限制,看看我们还可以做些什么。
模板字符串不会自动转义特殊字符,但是我们可以通过标签模板来解决这个问题,事实上我们还可以将 SaferHTML 这个方法写的更好。从安全角度来看,这个 SaferHTML 非常脆弱。在 HTML 中,不同的地方需要用不同的方式去转义,SaferHTML 并没有做到。稍加思考,我们就可以实现一个更加灵活的 SaferHTML方法,能够将 templateData 中的任何一个 HTML 转义,知道哪个占位符是纯 HTML;哪个是元素的属性,从而需要对 ' 和 " 转义;哪个是 URL 的 query 字符串,从而需要用 URL 的 escaping 方法,而不是 HTML 的 escaping;等等。这似乎有些牵强,因为 HTML 转义效率比较低。辛运是的,标签模板的字符串是保持不变的,SaferHTML 可以缓存已经转义过的字符串,从而提高效率。
模板字符串并没有内置的国际化特性,但通过标签模板,我们可以添加该特性。Jack Hsu 的文章详细介绍了实现过程,看下面例子:
i18n`Hello ${name}, you have ${amount}:c(CAD) in your bank account.` // => Hallo Bob, Sie haben 1.234,56 $CA auf Ihrem Bankkonto. i18n`Hello ${name}, you have ${amount}:c(CAD) in your bank account.` // => Hallo Bob, Sie haben 1.234,56 $CA auf Ihrem Bankkonto.
上面例子中的 name 和 amount 很好理解,将被 JS 引擎替换为对应的字符串,但是还有一个没有见过的占位符::c(CAD),这将被 i18n 标签处理,从 i18n 的文档可知::c(CAD)表示 amount 是加拿大美元货币值。
模板字符串不能替代 Mustache 和 Nunjucks 这类模板引擎,部分原因在于模板字符串不支持循环和条件语句。我们可以编写一个标签来实现这类功能:
// Purely hypothetical template language based on // ES6 tagged templates. var libraryHtml = hashTemplate` <ul> #for book in ${myBooks} <li><i>#{book.title}</i> by #{book.author}</li> #end </ul> `; // Purely hypothetical template language based on // ES6 tagged templates. var libraryHtml = hashTemplate` <ul> #for book in ${myBooks} <li><i>#{book.title}</i> by #{book.author}</li> #end </ul> `;
灵活性还不止于此,需要注意的是,标签函数的参数不会自动转换为字符串,参数可以是任何类型,返回值也一样。标签模板甚至可以不需要字符串,你可以使用自定义标签来创建正则表达式、DOM 树、图片、代表整个异步进程的 Promise、JS 数据结构、GL 着色器…
标签模板允许库设计者创建强大的领域特定语言。这些语言可能看上去并不像 JS,但他们可以无缝嵌入到 JS 中,并且可以与语言的其余部分进行交互。顺便说一下,我还没有在其他语言中见过类似的特性,我不知道这个特性讲给我们带来些什么,但各种可能性还是非常令人兴奋的。