一。作用及語法
模板字符串用來實現字符串插值,比加號(+)拼接更方便更優雅
`${expr}` 語法,提供了字符串插值功能
注意:上面的`(反撇號,位於鍵盤 Esc 下面)是模板字符串語法的一部分,而不是 markdown 用錯了
P.S. 筆者遇到了第一個問題,反撇號與 markdown 語法有衝突,有兩種選擇,要麼使用 markdown 特性之多行反撇好作為代碼分隔符(用兩個連續反撇號包裹代碼,但筆者使用的 markdown 插件不支持該罕見語法),要麼使用 code 標籤,比如上面的 markdown 源碼是<code>\`${expr}\`</code>
例如:
function req(user, action) {
// if !isValid
error(`user ${user} failed to do action ${action}`);
}
function error(str) {
console.log(str);
}
// test
req('Sam', 'login'); // user Sam failed to do action login
二。特點
###1.expr 可以是任意合法 js 表達式,且支持嵌套
包括變量、字面量、函數調用等等,但不支持流程控制(分支/循環),所以模板字符串不能代替模板引擎
例如:
// 不支持分支/循環
// console.log(`${if (2 > 1) {str}}`); // Uncaught SyntaxError: Unexpected token if
// console.log(`${for (; false;) {}}`); // Uncaught SyntaxError: Unexpected token for
var outer = 'outer';
var inner = 'inner';
var str = `
\$\{
${
outer // 變量
+ // + 拼接
`\$\{
${inner} // 插值
\}`
}
\}
`;
var h5 = document.querySelector('#h5');
var pre = document.querySelector('#pre');
h5.innerHTML = str;
pre.innerHTML = str;
顯示結果如下:
// #h5
${ outer${ inner // 插值 } }
// #pre
${
outer${
inner // 插值
}
}
###3. 可以分多行書寫
但註釋(單行/多行)可能會作用於整個串,或者直接作為字面量出現在結果串裡。模板字符串中所有的空格、新行、縮進,都會原樣輸出在生成的字符串中(pre 與其它元素不同),具體見上例
###4. 特殊字符需要轉義
3 個特殊字符[`${](正則表達式形式)需要轉義,具體見上例
P.S. 右花括號不需要轉義,當然,轉義它也沒錯。但是上面 3 個必須轉義
###5. 沒有自動處理
不會自動過濾不安全標籤,也不會自動轉義特殊字符,同樣需要防止 xss 攻擊,雖然提供了標籤模板(tagged templates),但不很方便
例如:
// xss 攻擊
var userInput = '<a href="" onclick="alert(\'xss attack\');">領取獎勵</a>';
var input = document.querySelector('#input');
input.innerHTML = `用戶輸入:${userInput}`;
上面代碼會生成一個鏈接,點擊 alert "xss attack",通過標籤模板可以對模板字符串進行自定義處理,比如濾掉不安全字符,如下:
// 定義模板標籤
function myFilter(templateData) {
var aStr = templateData; // 被${} 插值分割後的字符串數組
var aRaw = templateData.raw; // 未經轉義的原字符串(\n)
var aVar = Array.prototype.slice.call(arguments, 1); // 插值變量數組
console.log(aStr);
console.log(aRaw);
console.log(aVar);
// filter vars
aVar.forEach(function(item, index, arr) {
arr[index] = item.replace(/(<[^>]*>)/i, '');
});
var res = '';
var i = 0;
aStr.forEach(function(item) {
res += item;
if (aVar[i]) {
res += aVar[i++];
}
});
return res;
}
// 使用標籤模板
var safeInput = document.querySelector('#safe_input');
safeInput.innerHTML = myFilter`用戶輸入:\n${userInput}`;
// 等價於
// safeInput.innerHTML = myFilter(['用戶輸入:'], userInput);
標籤出現在模板字符串前,是一種簡化的函數調用,模板標籤類似於回調函數,參數 templateData 的格式是確定的,從參數中取出數據,處理完畢返回即可
但定義模板標籤感覺還是比較麻煩,但這是一個很有吸引力的特性,預示著一個強大的字符串處理庫(轉義特殊字符、i18n、字數統計等等)
###6. 靈活性
標籤模板的返回值不一定是字符串,可以是任何值。這意味著不僅僅會出現一個強大的「字符串」處理庫,任何庫都可以以這種形式完美嵌入JS 內,尤其是著色器語言這樣的目前在 JS 中形式不太友好的東西……其它語言出現誘人的特性後,可以完美植入 JS,甚至可以藉此實現一種自己的語言(模板標籤的函數體就是解釋器)
標籤模板帶來很大的靈活性,可以用自定義的標籤來創建正則表達式、DOM 樹、圖片、以 promises 為代表的整個異步過程、JS 數據結構、GL 著色器……
引用參考資料中的原文:
標籤模板以開放的姿態歡迎庫設計者們來創建強有力領域特定語言。這些語言可能看起來不像 JS,但是它們仍可以無縫嵌入到 JS 中並與 JS 的其它語言特性智能交互。我不知道這一特性將會帶領們走向何方,但它蘊藏著無限的可能性,這令我感到異常興奮!
三。總結
模板字符串看似語法糖,其實是打開了一扇大門,表示 JS 以開放的姿態迎接未來
還記得 [前輩們 5 年前討論的 yield](/articles/javascript 實現 yield/) 嗎?那時候他們多希望有個這樣的特性啊,討論這個問題的初衷不就是希望 JS 能獲得 C# 的 yield 特性嗎?
參考資料
- 《ES6 in Depth》:InfoQ 中文站提供的免費電子書
暫無評論,快來發表你的看法吧