0%

JS 樣板字面值 Template Literal、標籤樣板字面值 Tagged Template Literals

此篇會介紹 ES6 新增的「板字面值 Template Literal」。

樣板字面值 Template Literal

MDN:用來進行多行字串、字串串接以及字串插補(string interpolation),在先前的版本 ES2015 中被稱為「樣板字串(template strings)」。

語法:

`string text ${expression} string text`

兩個反引號``並加上 ${expression} 來插入變數、表達式。

e.g. 樣板自面值
1
2
let s = '我';
console.log(`${s}是誰,${s}在哪裡`);

Tips

  • 反引號的內容會保留所有的 換行空白
  • 不能插入陳述式,否則會噴 Error。
1
2
console.log(`我是誰  ,\n  我在哪裡`);
console.log(`這是陳述式範例:變數宣告${ let str}`); // SyntaxError: Missing } in template expression

Q:那為什麼會需要樣板字面值?其它方式不行嗎?
A:可以,但樣板字面值簡化串接過程,使串接變得更容易簡潔

下面會透過幾個範例,來演示舊字串串接、樣板字面值兩者在不同情境的差別。

語法差異

舊字串串接:會使用單引號雙引號包裹字串,再透過 + 的方式來串接。
樣板字面值:兩個反引號 `` 並加上 ${expression} 來插入變數、表達式。

e.g. 新舊寫法差異
1
2
3
4
5
6
7
8
let str1 = '世界';
let str2 = '和平';

// 舊字串串接
console.log(str1 + str2 + ' & 愛');

// 樣板字面值
console.log(`${str1}${str2} & 愛`);

多行串接

e.g. 多行串接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let money = 10;

// 舊字串串接(需要使用到反斜線來換行)
let template = '<ul>\
<li>' + money + '元 </li>\
<li>' + money + '元 </li>\
</ul>';

// 樣板字面值(會保留所有換行和空白)
let template = `
<ul>
<li> ${money}元 </li>
<li> ${money}元 </li>
</ul>
`;

跳脫字元

Q:那如果需要將反引號作為字串怎麼辦?
A:可以使用跳脫字元。

e.g. 跳脫字元
1
2
// 在字元前面加上反斜線(backslash)
console.log(`我厶\`誰`); // 我厶`誰

複雜用法

上面範例中已充分暸解如何使用樣板字面值,來做字串串接和字串插補,下方在用一個範例來演示,結合函式、判斷式、巢狀的應用。

e.g. 函式 + 判斷式 + 巢狀
1
2
3
4
5
6
7
8
9
10
11
let moneys = [10, 0];
let template = `<ul>
${moneys.map((money) => {
if (money) {
return `<li> ${money}元 </li>`;
} else {
return `<li> 沒錢 </li>`;
}
}).join('')
}
</ul>`;

不敢想像用傳統串接寫的話會變成什麼樣子 QQ


標籤樣板字面值 Tagged Template Literals

MDN:屬於樣板字面值的進階用法,常用於函式參數傳入。

語法:

  • 函式後方將括號替代為樣板字面值
e.g. 簡化了一般函式呼叫的括號
1
2
3
4
5
// 一般函式參數
console.log('標籤樣板字面值');

// 標籤樣板字面值
console.log`標籤樣板字面值`;

如果樣板字面值中沒有使用到 ${},那和一般函式呼叫帶入的參數沒什麼差異,那看起來也沒多厲害?不不不精彩的正要開始。

樣板字面值傳參數特性

  • 將樣板字面值內容中含有 ${} 切為 多個參數,將其組成一個陣列傳入函式中。

參數格式

  • 第一個參數(陣列):除了 ${} 之外的內容。
    • ${} 位置切割,並會以字串型態存入陣列中。
    • 要注意就算 ${} 後面沒有其他字串,但還是會有一個空字串存入陣列中
  • 第二個參數:第一個 ${}
  • 第三個參數:第二個 ${}(…依此類推下去)

${} 後面沒有其他字串,但還是會有一個空字串存入陣列中。
例如:一個 ${} 會切兩個字串,兩個 ${} 會切三個字串。

e.g. 標籤樣板字面值
1
2
3
4
5
6
7
8
let str1 = '標籤';
let str2 = '標籤樣板字面值';
function fn(str, arg) {
console.log(str); // str=['演示', '', '範例']
console.log(arg); // arg='標籤'
}

fn`演示${str1}${str2}範例`;

變數的部分,也可以使用其餘運算子來一次接收全部變數。

e.g. 其餘運算子接收所有變數
1
2
3
4
5
6
7
8
let str1 = '標籤';
let str2 = '標籤樣板字面值';
function fn(str, ...arg) {
console.log(str); // str=['演示', '', '範例']
console.log(arg); // arg=['標籤', '標籤樣板字面值']
}

fn`演示${str1}${str2}範例`;

通常 ${} 數量是不固定的,因此可以使用其餘運算子將 ${} 的參數組成陣列來使用。

e.g. 標籤樣板字面值 + 其餘運算子
1
2
3
4
5
6
7
8
9
let str1 = '標籤樣板字面值';
let str2 = '其餘運算子';

function fn(str, ...args) {
console.log(str, args);
// str=['演示', '+', '範例'], arg=['標籤樣板字面值', '其餘運算子']
}

fn`演示${str1}+${str2}範例`;

要注意第一個參數永遠只會傳入非 ${} 的字串,所以請不要在第一個參數使用其餘參數,因為沒有必要。

標籤樣板字面值也常用來預防 XSS 攻擊,請看下方範例。

  • 透過將 ${} 內容中含有<>& 的符號替換成 ASCII Code,也就是單純的字串。
e.g. 預防 XSS 攻擊src
1
2
3
4
5
function convertHTML (strings, ...keys) {
return strings.map((str, i) => `${str}${keys[i] ? keys[i].replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'): ''}`).join('');
}

convertHTML`<p>超連結:${Url}</p>`;

reference