前面的話
JS
的字符串相對(duì)其他語言來說功能總是有限的,事實(shí)上,ES5
中一直缺乏許多特性,如多行字符串、字符串格式化、HTML
轉(zhuǎn)義等。ES6
通過模板字面量的方式進(jìn)行了填補(bǔ),模板字面量試著跳出JS
已有的字符串體系,通過一些全新的方法來解決類似的問題。本文將詳細(xì)介紹ES6
模板字面量
基本用法
模板字面量是增強(qiáng)版的字符串,它用反引號(hào)(`)標(biāo)識(shí)
let message = `Hello world!`;
console.log(message); // "Hello world!"
console.log(typeof message); //"string"
console.log(message.length);// 12
以上代碼中,使用模板字面量語法創(chuàng)建一個(gè)字符串,并賦值給message變量,這時(shí)變量的值與一個(gè)普通的字符串無異
如果想在字符串中包含反引號(hào),只需使用反斜杠(\ )轉(zhuǎn)義即可
let message = `\`Hello\` world!`;
console.log(message); // "`Hello` world!"
console.log(typeofmessage);//"string"
console.log(message.length);// 14
多行字符串
自
javascript
誕生起,開發(fā)者們就一直在尋找一種能創(chuàng)建多行字符串的方法。如果使用雙引號(hào)或單引號(hào),字符串一定要在同一行才行
【反斜杠】
由于
javascript
長期以來一直存在一個(gè)語法bug
,在換行之前的反斜線(\ )可以承接下一行的代碼,于是可以利用這個(gè)bug
來創(chuàng)建多行字符串
var message = "Multiline \string";
console.log(message); // "Multiline string"
message字符串打印輸出時(shí)不會(huì)有換行,因?yàn)榉葱本€被視為延續(xù)符號(hào)而不是新行的符號(hào)。為了在輸出中顯示換行,需要手動(dòng)加入換行符
var message = "Multiline \n\string";
// "Multiline // string"
console.log(message);
在所有主流的JS 引擎中,此代碼都會(huì)輸出兩行,但是該行為被認(rèn)定為一個(gè)bug ,并且許多開發(fā)者都建議應(yīng)避免這么做
在ES6之前,通常都依靠數(shù)組或字符串的拼接來創(chuàng)建多行字符串
var message = ["Multiline ","string"].join("\n");
let message = "Multiline \n" +"string";
JS一直以來都不支持多行字符串,開發(fā)者的種種解決方法都不夠完美
【反引號(hào)】
ES6
的模板字面量使多行字符串更易創(chuàng)建,因?yàn)樗恍枰厥獾恼Z法,只需在想要的位置直接換行即可,此處的換行會(huì)同步出現(xiàn)在結(jié)果中
let message =`Multiline string`;
// "Multiline// string"
console.log(message);
console.log(message.length); // 16
在反引號(hào)之內(nèi)的所有空白符都是字符串的一部分,因此需要特別留意縮進(jìn)
let message = `Multiline string`;
// "Multiline // string"
console.log(message);
console.log(message.length); //24
- 以上代碼中,模板字面量第二行前面的所有空白符都被視為字符串自身的一部分
- 如果一定要通過適當(dāng)?shù)目s進(jìn)來對(duì)齊文本,可以考慮在多行模板字面量的第一行空置并在后面的幾行縮進(jìn)
let html = `<div>`
    
`<h1>Title</h1>`
</div>.trim();
以上代碼中,模板字面量的第一行沒有任何文本,第二行才有內(nèi)容。HTML標(biāo)簽的縮進(jìn)增強(qiáng)了可讀性,之后再調(diào)用trim()方法移除了起始的空行
當(dāng)然,也可以在模板字面量中使用\n 來指示換行的插入位置
let message =`Multiline\nstring`;
// "Multiline// string"
console.log(message);
console.log(message.length); // 16
變量占位符
模板字面量看上去僅僅是普通
JS
字符串的升級(jí)版,但二者之間真正的區(qū)別在于模板字面量的變量占位符。變量占位符允許將任何有效的JS
表達(dá)式嵌入到模板字面量中,并將其結(jié)果輸出為字符串的一部分
變量占位符由起始的 ${ 與結(jié)束的 }來界定,之間允許放入任意的JS
表達(dá)式。最簡單的變量占位符允許將本地變量直接嵌入到結(jié)果字符串中
let name = "Nicholas",
message =`Hello, ${name}.`;
console.log(message); // "Hello, Nicholas."
占位符 ${name} 會(huì)訪問本地變量
name
,并將其值插入到message
字符串中。message
變量會(huì)立即保留該占位符的結(jié)果
既然占位符是JS
表達(dá)式,那么可替換的就不僅僅是簡單的變量名。可以輕易嵌入運(yùn)算符、函數(shù)調(diào)用等
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 表達(dá)式,因此可以將模板字面量嵌入到另一個(gè)模板字面量內(nèi)部
let name = "Nicholas",
message =`Hello, ${
???????`my name is ${ name }`
}.`;
console.log(message); // "Hello, my name is Nicholas."
標(biāo)簽?zāi)0?/h3>
模板字面量真正的威力來自于標(biāo)簽?zāi)0澹總€(gè)模板標(biāo)簽都可以執(zhí)行模板字面量上的轉(zhuǎn)換并返回最終的字符串值。標(biāo)簽指的是在模板字面量第一個(gè)反引號(hào)'`'前方標(biāo)注的字符串
let message = tag`Hello world`;
模板字面量真正的威力來自于標(biāo)簽?zāi)0澹總€(gè)模板標(biāo)簽都可以執(zhí)行模板字面量上的轉(zhuǎn)換并返回最終的字符串值。標(biāo)簽指的是在模板字面量第一個(gè)反引號(hào)'`'前方標(biāo)注的字符串
let message = tag`Hello world`;
在這個(gè)示例中,tag 就是應(yīng)用到`Hello world`
模板字面量上的模板標(biāo)簽
【定義標(biāo)簽】
標(biāo)簽可以是一個(gè)函數(shù),調(diào)用時(shí)傳入加工過的模板字面量各部分?jǐn)?shù)據(jù),但必須結(jié)合每個(gè)部分來創(chuàng)建結(jié)果。第一個(gè)參數(shù)是一個(gè)數(shù)組,包含
Javascript
解釋過后的字面量字符串,它之后的所有參數(shù)都是每一個(gè)占位符的解釋值
標(biāo)簽函數(shù)通常使用不定參數(shù)特性來定義占位符,從而簡化數(shù)據(jù)處理的過程
function tag(literals, ...substitutions) {
// 返回一個(gè)字符串
}
為了進(jìn)一步理解傳遞給tag函數(shù)的參數(shù),查看以下代碼
let count = 10,
price = 0.25,
message = passthru`${count} items cost $${(count * price).toFixed(2)}.`;
如果有一個(gè)名為passthru()的函數(shù),那么作為一個(gè)模板字面量標(biāo)簽,它會(huì)接受3個(gè)參數(shù)首先是一個(gè)literals數(shù)組,包含以下元素
1、第一個(gè)占位符前的空字符串("")
2、第一、二個(gè)占位符之間的字符串("items cost $")
3、第二個(gè)占位符后的字符串(".")下一個(gè)參數(shù)是變量count的解釋值,傳參為10,它也成為了substitutions數(shù)組里的第一個(gè)元素
最后一個(gè)參數(shù)是(count*price).toFixed(2)的解釋值,傳參為2.50,它是substitutions數(shù)組里的第二個(gè)元素
[注意]literals里的第一個(gè)元素是一個(gè)空字符串,這確保了literals[0]總是字符串的始端,就像literals[literals.length-1]總是字符串的結(jié)尾一樣。substitutions的數(shù)量總比literals少一個(gè),這也意味著表達(dá)式substitutions.Iength === literals. Iength-1的結(jié)果總為true
vara = 5;varb = 10;
tag`Hello ${ a + b } world ${ a * b }`; // 等同于tag(['Hello ', ' world ', ''], 15, 50);
通過這種模式,我們可以將literals和substitutions兩個(gè)數(shù)組交織在一起重組結(jié)果字符串。先取出literals中的首個(gè)元素,再取出substitution中的首個(gè)元素,然后交替繼續(xù)取出每一個(gè)元素,直到字符串拼接完成。于是可以通過從兩個(gè)數(shù)組中交替取值的方式模擬模板字面量的默認(rèn)行為
function passthru(literals, ...substitutions) {
let result = ""; // 僅使用 substitution 的元素?cái)?shù)量來進(jìn)行循環(huán)
for(let i = 0; i < substitutions.length; i++) {
result +=literals[i];
result +=substitutions[i];
} // 添加最后一個(gè)字面量``
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."
這個(gè)示例定義了一個(gè)passthru標(biāo)簽,模擬模板字面量的默認(rèn)行為,展示了一次轉(zhuǎn)換過程。此處的小竅門是使用substitutions.length來為循環(huán)計(jì)數(shù)
【應(yīng)用】
“標(biāo)簽?zāi)0濉?/strong>的一個(gè)重要應(yīng)用,就是過濾
HTML
字符串,防止用戶輸入惡意內(nèi)容
var message = SaferHTML` ${sender} has sent you a message.`;
function SaferHTML(templateData) {
var s = templateData[0];
for(vari = 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變量往往是用戶提供的,經(jīng)過SaferHTML函數(shù)處理,里面的特殊字符都會(huì)被轉(zhuǎn)義
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>
標(biāo)簽?zāi)0宓牧硪粋€(gè)應(yīng)用,就是多語言轉(zhuǎn)換(國際化處理)
i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!`// "歡迎訪問xxx,您是第xxxx位訪問者!"
模板字符串本身并不能取代模板引擎,因?yàn)闆]有條件判斷和循環(huán)處理功能,但是通過標(biāo)簽函數(shù),可以自己添加這些功能
// 下面的hashTemplate函數(shù) // 是一個(gè)自定義的模板處理函數(shù)
var libraryHtml = hashTemplate`
<ul>
#for book in ${myBooks}<li><i>#{book.title}</i>by #{book.author}</li>#end
</ul>;
raw()
String.raw
方法,往往用來充當(dāng)模板字面量的處理函數(shù),返回一個(gè)斜杠都被轉(zhuǎn)義(即斜杠前面再加一個(gè)斜杠)的字符串,對(duì)應(yīng)于替換變量后的模板字面量
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!'
如果原字符串的斜杠已經(jīng)轉(zhuǎn)義,那么String.raw不會(huì)做任何處理
String.raw`Hi\\n` // "Hi\\n"
- String.raw方法可以作為處理模板字面量的基本方法,它會(huì)將所有變量替換,而且對(duì)斜杠進(jìn)行轉(zhuǎn)義,方便下一步作為字符串來使用。
- String.raw方法也可以作為正常的函數(shù)使用。這時(shí),它的第一個(gè)參數(shù),應(yīng)該是一個(gè)具有raw屬性的對(duì)象,且raw屬性的值應(yīng)該是一個(gè)數(shù)組
String.raw({ raw: 'test' }, 0, 1, 2);// 't0e1s2t' // 等同于String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);
其他章節(jié)
- ES6-數(shù)字?jǐn)U展
- ES6-字符串拓展
- ES6-模板字面量
- ES6-關(guān)于Unicode的相關(guān)擴(kuò)展
- ES6-正則表達(dá)式擴(kuò)展
- ES6-函數(shù)擴(kuò)展
- ES6-對(duì)象擴(kuò)展
- ES6-Symbol
- ES6-Set和Map集合
- ES6-數(shù)組擴(kuò)展
- ES6-定型數(shù)組
- ES6-塊級(jí)作用域
- ES6-解構(gòu)賦值
- ES6-類
- ES6-代理(Proxy)和反射(Reflection)
- ES6-ES6中的模塊
- ES6-ES2017中的修飾器Decorator
- ES6-迭代器(Iterator)和生成器(Generator)
- ES6-Promise和異步編程
- ES6-ES2017中的async