【探祕ES6】系列專欄(四):模版字符串

ES6做爲新一代JavaScript標準,即將與廣大前端開發者見面。爲了讓你們對ES6的諸多新特性有更深刻的瞭解,Mozilla Web開發者博客推出了《ES6 In Depth》系列文章。CSDN已獲受權,將持續對該系列進行翻譯,組織成【探祕ES6】系列專欄供你們學習借鑑。本文爲該系列的第四篇。html

前兩次學習了生成器迭代器之後,腦殼有沒有一團漿糊?哈哈。我承諾過本次咱們將學習一些簡單的東西。前端

那咱們如今就開始吧!git

「小句號」的基本使用程序員

ES6新引入了一種新的字符串語法——模版字符串(Template Strings),它看起來和普通的字符串很像,區別在於它不是由單引號'或者雙引號"來閉合,而是使用`(俗稱:小句號)。咱們來看個最簡單的例子,它們其實就是字符串而已:es6

context.fillText(`Ceci n'est pas une chaîne.`, x, y);

可是既然它被叫作「模版字符串」,那它應該不只僅是用小句號來閉合的普通字符串吧?模版字符串讓JavaScript有了一個簡單的 字符串插值 功能——既寫法美觀又能很方便地將JavaScript變量替換到字符串中。

不少情景下均可以用到它,但最打動個人是它在絕不起眼的錯誤消息中的使用:github

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 jorendorff is not authorized to do hockey(你能夠本身試試,我可沒忽悠你)。

目前爲止,它只是個比+(加號鏈接符)在語法上略微美觀一些而已,你可能想了解更多關於它的細節:web

  • 模版替換位中的代碼能夠是任何JavaScript的表達式,好比函數調用、算數運算等等都是容許的(只要你樂意,你甚至能夠在模版字符串中嵌套模版字符串,有種模版中的《盜夢空間》的感受)。正則表達式

  • 若是插入的值不是字符串,它將被轉換爲字符串。例如,若是action是個對象,它的.toString()方法將會被調用。typescript

  • 若是你的模版字符串中包含 ` (小句號),須要使用轉義字符`\``,效果至關於 "`"。express

  • 觸類旁通,若是你的模版字符串中須要 ${ 這兩個字符,我不想去關心你爲何要這麼寫,可是你可使用轉義其中任意一個字符:`write \${  或者 $\{`。

與普通字符串不一樣的是,模版字符串能夠寫成多行:

$("#warning").html(`  
  <h1>Watch out!</h1>  
  <p>Unauthorized hockeying can result in penalties  
  of up to ${maxPenalty} minutes.</p>  
`);

在模版字符串中,全部空格、換行、縮進都是逐字輸出的。

好的,就像我上篇文章所承諾的,我以爲我必須對你大腦的健康負責。因此給一個小小的警告:接下來的內容就比較費腦一點了。你能夠如今就放棄閱讀,喝杯咖啡享受一下,放鬆一下大腦。認真地說,不用爲放棄下面的內容而感到羞愧噢。 當Lopes Gonçalves穿過了赤道,證實了不會被可怕的海怪吃掉或掉進地球的盡頭,而後他還須要去航行整個完整的南半球來增長論證嗎?不須要!他返航了,回到家吃了頓美美的午飯。換作是你也會這麼作,對不對?

深刻討論「小句號」

咱們來討論一些模版字符串的侷限性。

  • 它不會爲你自動轉義關鍵字。爲了防止腳本注入,你須要認真當心對待那些不受信任的數據,就像你曾經當心地拼接字符串同樣。

  • 它並無明確地說明如何配合國際化庫(一個針對用戶所處的國家/語言不一樣來國際化你的代碼的庫)來使用。模版字符串不會處理特定語言的數字、日期等的格式化。

  • 它並不能代替像MustacheNunjucks這樣的模板庫。

模版字符串沒有內置針對循環的語法——經過數組來循環生成一個HTML表格;甚至條件語句也不支持。(固然,你可使用模版嵌套來實現,可是這樣的方法太笨拙,我認爲不可取。)

ES6在模版字符串中爲JS開發者和庫設計人員提供了一種解決方案來幫助他們處理這些侷限性問題。這個特性被稱作模版標記。

模版標記的語法很簡單。咱們只須要在「小句號」的前面加一個額外的標記。在咱們接下來的第一個例子中,SaferHTML就是它的標記,咱們使用這個標記來處理上面列表中的第一個侷限性問題:自動轉義關鍵字。

必須清楚SaferHTML並非ES6標準庫中的內容。在下面咱們將會本身來實現它。

var message =  
  SaferHTML`<p>${bonk.sender} has sent you a bonk.</p>`;

這裏使用了一個標識符SaferHTML來做爲標記,標記也能夠是一個屬性——SaferHTML.escape,甚至能夠是一個函數調用——SaferHTML.escape({unicodeControlCharacters: false})。(說明一下,任何ES6的 MemberExpressio類或CallExpression類 均可以做爲標記使用)。

能夠看出不含標記的模版字符串適用於簡單的字符串拼接。標記模版徹底適用於其它場景:如函數調用。

上面的代碼與這段等效:

var message =  
  SaferHTML(templateData, bonk.sender);

templateData 是由模版的字符串部分所組成的不可變數組,由JS引擎爲咱們提供。下面是一個擁有兩個元素的數組,由於在標記模板中他們被模版替換所分割,有兩個字符串部分。因此templateData 應該是這個樣子的:  Object.freeze (["<p>", " has sent you a bonk.</p>"]。

(實際上templateData 還有一個屬性。我並不打算在這篇文章中用到它,爲了完整性我仍是提一下: templateData.raw 是另外一個囊括了標記模版中全部字符串部分的數組,它的源碼就和它的名稱(raw)同樣原始——還保留着\n這樣的轉義序列,而不是被轉到新的一行等。標準的String.raw使用了最原始的字符串。)

這樣就給了SaferHTML函數很大的自由度,能夠用不少可選的方法去解析字符串和替換字符。

如今你必定想弄清楚SaferHTML到底是怎麼實現的,或許你想親手來嘗試實現它。它終究只是個函數而已。你能夠再Firefox的控制檯中測試一下。

下面是其中一種方式(查看Gist上的例子)。

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`<p>${bonk.sender} has sent you a bonk.</p>`將會被解析爲 "<p>ES6&lt;3er has sent you a bonk.</p>"。那麼你的用戶傳入的值都是安全的,即便有人惡意地將bonk.sender 賦值爲 "Hacker Steve <script>alert('xss');</script>",返回的也是不可執行的字符串,不管這段字符串的含義是什麼,都不會有安全漏洞。

(順便說一句,若是你以爲函數使用參數對象的方式讓你感受這樣很笨重,咱們下次將換用另外一種方法。ES6還有另外一個我認爲你會喜歡的新特性。)

一個例子並不能說明標記模版的靈活性。讓咱們再來看看咱們能爲以前列出的那些模版字符串的侷限性作些什麼改進。

  • 模版字符串不自動轉義特殊字符。可是咱們已經看到了,使用標記模版咱們就能解決這個問題。實際上,咱們能作得更好。

從安全性的角度來看,個人SaferHTML函數功能性很是弱。HTML中不一樣的地方有不一樣的關鍵字須要不一樣的轉義方式。SaferHTML把它們所有都轉義,可是咱們能夠再付出一些努力把SaferHTML寫的更精明一點,徹底按照字節來解析templateData中的字符串,這樣咱們就知道哪些替換位是在純HTML中;哪些是在元素屬性中,這些 ‘ 和 「 就須要轉義;那些在URL查詢字符串中須要使用URL轉義而不是HTML轉義等等。它能夠準確地對每一個替代位進行正確的轉義。

也許你會有疑問——HTML解析效率低,這方法是否是很牽強?幸運的是標記模版的字符串部分是固定不變的。SaferHTML能夠將這部分的解析結果緩存起來,這樣就能夠提升運行速度了。(這個緩存能夠是一個WeakMap,WeakMap是ES6的新特性,咱們在之後的文章中將會對它有所討論。)

  • 模版字符串沒有涉及國際化相關的特性。好在有了標記,這個問題就迎刃而解。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.


這就是標記模板的做用了。

  • 它並不能代替像Mustache 、Nunjucks這樣的模板庫,大部分緣由是由於它沒有內置對循環和條件句的支持。那麼如今咱們一塊兒來看看如何解決這個問題,準備好了嗎?

若是JS沒有爲咱們提供某個特性,那咱們就本身寫一個!

// 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樹、圖片、完整的異步流程、JS數據結構、GL着色器等等。

標記模版鼓勵庫開發人員去建立強大的基於特定領域的語言。這些語言看起來一點也不像JS,可是他們能夠無縫地嵌入JS中而且能夠智能地與其它語言進行交互。如今,我想不出其它任何有相似特性的語言,我不知道這個特性未來會給咱們的開發帶來什麼改變,但這個改變應該是使人驚喜的。

我何時能夠開始使用模版字符串?

在服務端,io.js已經開始支持模版字符串了。

瀏覽器裏,FireFox 34+已經支持模版字符串。去年夏天Guptha Rajagopal已經把它做爲一個實習項目。Chrome 41+也支持模板字符串,IE和Safari不支持。如今,若是你想在web項目中使用模版字符串你須要用到BabelTraceur來支持;你也能夠在TypeScript中使用。

在Markdown中可使用嗎?

嗯? 

哦……這是一個好問題。

(這一部分與JavaScript無關。若是你不使用Markdown,能夠跳過這一段。)

Markdown和JavaScript都使用 ` 來做爲模版字符串的特殊字符。實際上,在Markdown中

它是內聯文本的定界符。

若是你在Markdown中用下面的方式來寫是會有問題的:

To display a message, write `alert(`hello world!`)`.

顯示的結果爲:

To display a message, write alert(hello world!).

請注意,在輸出中是不會有小句號的。Markdown把這個四個小句號都解釋爲定界符,在輸出結果中被HTML標記替換掉了。

爲了不這種狀況,咱們一開始在Markdown就採用了一種比較少見的特性:使用多重小句號來做爲代碼定界符,以下:

To display a message, write ``alert(`hello world!`)``.

能夠到 Gist 上去查看更多細節,它是由Markdown寫的,你能夠查看源碼。

下期預告

下篇文章,咱們將學習兩個新特性,在其它語言中這兩個特性已經被程序員們愉快地使用了幾十年了:其中一個是爲了那些喜歡儘可能避免使用參數的開發人員準備的,另外一個是給那些喜歡使用不少參數的人準備的。固然,兩種特性都爲咱們提供了,咱們能夠根據我的對函數的使用習慣來選擇。

這些特性能夠直接在Firefox中測試,因此下次咱們一塊兒來試一下吧,咱們的客座來賓Benjamin Peterson將爲你們講解ES6的default parameters和rest parameters。(譯者:向渝 責編:陳秋歌)

原文連接:ES6 In Depth: Template strings

本譯文遵循Creative Commons Attribution Share-Alike License v3.0 

相關文章
相關標籤/搜索