[1]基本用法 [2]多行字元串 [3]變數占位符 [4]標簽模板 [5]raw() ...
前面的話
JS 的字元串相對其他語言來說功能總是有限的,事實上,ES5中一直缺乏許多特性,如多行字元串、字元串格式化、HTML轉義等。ES6通過模板字面量的方式進行了填補,模板字面量試著跳出JS已有的字元串體系,通過一些全新的方法來解決類似的問題。本文將詳細介紹ES6模板字面量
基本用法
模板字面量是增強版的字元串,它用反引號(`)標識
let message = `Hello world!`; console.log(message); // "Hello world!" console.log(typeof message); // "string" console.log(message.length); // 12
以上代碼中,使用模板字面量語法創建一個字元串,並賦值給message變數,這時變數的值與一個普通的字元串無異
如果想在字元串中包含反引號,只需使用反斜杠( \ )轉義即可
let message = `\`Hello\` world!`; console.log(message); // "`Hello` world!" console.log(typeof message); // "string" console.log(message.length); // 14
多行字元串
自javascript誕生起,開發者們就一直在尋找一種能創建多行字元串的方法。如果使用雙引號或單引號,字元串一定要在同一行才行
【反斜杠】
由於javascript長期以來一直存在一個語法bug,在換行之前的反斜線( \ )可以承接下一行的代碼,於是可以利用這個bug來創建多行字元串
var message = "Multiline \ string"; console.log(message); // "Multiline string"
message 字元串列印輸出時不會有換行,因為反斜線被視為延續符號而不是新行的符號。為了在輸出中顯示換行,需要手動加入換行符
var message = "Multiline \n\ string"; // "Multiline // string" console.log(message);
在所有主流的 JS 引擎中,此代碼都會輸出兩行,但是該行為被認定為一個 bug ,並且許多開發者都建議應避免這麼做
在ES6之前,通常都依靠數組或字元串的拼接來創建多行字元串
var message = ["Multiline ","string"].join("\n"); let message = "Multiline \n" +"string";
JS一直以來都不支持多行字元串,開發者的種種解決方法都不夠完美
【反引號】
ES6 的模板字面量使多行字元串更易創建,因為它不需要特殊的語法,只需在想要的位置直接換行即可,此處的換行會同步出現在結果中
let message = `Multiline string`; // "Multiline // string" console.log(message); console.log(message.length); // 16
在反引號之內的所有空白符都是字元串的一部分,因此需要特別留意縮進
let message = `Multiline string`; // "Multiline // string" console.log(message); console.log(message.length); //24
以上代碼中,模板字面量第二行前面的所有空白符都被視為字元串自身的一部分
如果一定要通過適當的縮進來對齊文本,可以考慮在多行模板字面量的第一行空置併在後面的幾行縮進
let html = `
<div>
<h1>Title</h1>
</div>`.trim();
以上代碼中,模板字面量的第一行沒有任何文本,第二行才有內容。 HTML標簽的縮進增強了可讀性,之後再調用trim()方法移除了起始的空行
當然,也可以在模板字面量中使用 \n 來指示換行的插入位置
let message = `Multiline\nstring`; // "Multiline // string" console.log(message); console.log(message.length); // 16
變數占位符
模板字面量看上去僅僅是普通JS字元串的升級版,但二者之間真正的區別在於模板字面量的變數占位符。變數占位符允許將任何有效的JS表達式嵌入到模板字面量中,並將其結果輸出為字元串的一部分
變數占位符由起始的 ${ 與結束的 } 來界定,之間允許放入任意的 JS 表達式。最簡單的變數占位符允許將本地變數直接嵌入到結果字元串中
let name = "Nicholas", message = `Hello, ${name}.`; console.log(message); // "Hello, Nicholas."
占位符 ${name} 會訪問本地變數 name ,並將其值插入到 message 字元串中。 message變數會立即保留該占位符的結果
既然占位符是JS表達式,那麼可替換的就不僅僅是簡單的變數名。可以輕易嵌入運算符、函數調用等
let count = 10, price = 0.25, message = `${count} items cost $${(count * price).toFixed(2)}.`; console.log(message); // "10 items cost $2.50."
function fn() { return "Hello World"; } `foo ${fn()} bar` // foo Hello World bar
模板字面量本身也是 JS 表達式,因此可以將模板字面量嵌入到另一個模板字面量內部
let name = "Nicholas", message = `Hello, ${ `my name is ${ name }` }.`; console.log(message); // "Hello, my name is Nicholas."
標簽模板
模板字面量真正的威力來自於標簽模板,每個模板標簽都可以執行模板字面量上的轉換並返回最終的字元串值。標簽指的是在模板字面量第一個反引號'`'前方標註的字元串
let message = tag`Hello world`;
在這個示例中, tag 就是應用到 `Hello world` 模板字面量上的模板標簽
【定義標簽】
標簽可以是一個函數,調用時傳入加工過的模板字面量各部分數據,但必須結合每個部分來創建結果。第一個參數是一個數組,包含Javascript解釋過後的字面量字元串,它之後的所有參數都是每一個占位符的解釋值
標簽函數通常使用不定參數特性來定義占位符,從而簡化數據處理的過程
function tag(literals, ...substitutions) { // 返回一個字元串 }
為了進一步理解傳遞給tag函數的參數,查看以下代碼
let count = 10, price = 0.25, message = passthru`${count} items cost $${(count * price).toFixed(2)}.`;
如果有一個名為passthru()的函數,那麼作為一個模板字面量標簽,它會接受3個參數首先是一個literals數組,包含以下元素
1、第一個占位符前的空字元串("")
2、第一、二個占位符之間的字元串(" items cost $")
3、第二個占位符後的字元串(".")
下一個參數是變數count的解釋值,傳參為10,它也成為了substitutions數組裡的第一個元素
最後一個參數是(count*price).toFixed(2)的解釋值,傳參為2.50,它是substitutions數組裡的第二個元素
[註意]literals里的第一個元素是一個空字元串,這確保了literals[0]總是字元串的始端,就像literals[literals.length-1]總是字元串的結尾一樣。substitutions的數量總比literals少一個,這也意味著表達式substitutions. Iength === literals. Iength-1的結果總為true
var a = 5; var b = 10; tag`Hello ${ a + b } world ${ a * b }`; // 等同於 tag(['Hello ', ' world ', ''], 15, 50);
通過這種模式,我們可以將literals和substitutions兩個數組交織在一起重組結果字元串。先取出literals中的首個元素,再取出substitution中的首個元素,然後交替繼續取出每一個元素,直到字元串拼接完成。於是可以通過從兩個數組中交替取值的方式模擬模板字面量的預設行為
function passthru(literals, ...substitutions) { let result = ""; // 僅使用 substitution 的元素數量來進行迴圈 for (let i = 0; i < substitutions.length; i++) { result += literals[i]; result += substitutions[i]; } // 添加最後一個字面量 result += literals[literals.length - 1]; return result; } let count = 10, price = 0.25, message = passthru`${count} items cost $${(count * price).toFixed(2)}.`; console.log(message); // "10 items cost $2.50."
這個示例定義了一個passthru標簽,模擬模板字面量的預設行為,展示了一次轉換過程。此處的小竅門是使用substitutions.length來為迴圈計數
【應用】
“標簽模板”的一個重要應用,就是過濾HTML字元串,防止用戶輸入惡意內容
var message = SaferHTML`<p>${sender} has sent you a message.</p>`; 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; }
上面代碼中,sender
變數往往是用戶提供的,經過SaferHTML
函數處理,裡面的特殊字元都會被轉義
var sender = '<script>alert("abc")</script>'; // 惡意代碼 var message = SaferHTML`<p>${sender} has sent you a message.</p>`; console.log(message);// <p><script>alert("abc")</script> has sent you a message.</p>
標簽模板的另一個應用,就是多語言轉換(國際化處理)
i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!` // "歡迎訪問xxx,您是第xxxx位訪問者!"
模板字元串本身並不能取代模板引擎,因為沒有條件判斷和迴圈處理功能,但是通過標簽函數,可以自己添加這些功能
// 下麵的hashTemplate函數 // 是一個自定義的模板處理函數 var libraryHtml = hashTemplate` <ul> #for book in ${myBooks} <li><i>#{book.title}</i> by #{book.author}</li> #end </ul> `;
raw()
String.raw
方法,往往用來充當模板字面量的處理函數,返回一個斜杠都被轉義(即斜杠前面再加一個斜杠)的字元串,對應於替換變數後的模板字面量
let message1 = `Multiline\nstring`, message2 = String.raw`Multiline\nstring`; console.log(message1); // "Multiline // string" console.log(message2); // "Multiline\\nstring"
String.raw`Hi\n${2+3}!`; // "Hi\\n5!" String.raw`Hi\u000A!`; // 'Hi\\u000A!'
如果原字元串的斜杠已經轉義,那麼String.raw
不會做任何處理
String.raw`Hi\\n`// "Hi\\n"
String.raw
方法可以作為處理模板字面量的基本方法,它會將所有變數替換,而且對斜杠進行轉義,方便下一步作為字元串來使用。
String.raw
方法也可以作為正常的函數使用。這時,它的第一個參數,應該是一個具有raw
屬性的對象,且raw
屬性的值應該是一個數組
String.raw({ raw: 'test' }, 0, 1, 2);// 't0e1s2t' // 等同於 String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);