跳到主要內容
黯羽輕揚每天積累一點點

模板字符串_ES6 筆記 3

免費2016-04-10#JS#JS模板字符串#JS反撇号#JS标签模板#template string#template inception#tagged template

ES6 中最為開放的特性,不只是拼接字符串那麼簡單

一。作用及語法

模板字符串用來實現字符串插值,比加號(+)拼接更方便更優雅

`${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 中文站提供的免費電子書

評論

暫無評論,快來發表你的看法吧

提交評論