前言
進階的用法:標籤樣板字面值
標籤樣板字面值
先用一個簡單的範例來帶大家了解什麼是標籤樣板字面值。
showConsole 這個函式會顯示接收到的字串以及參數,下方也有定義好的變數,以及事先準備好的樣板字面值字串。
function showConsole(strings, arg1) {
console.log(strings, arg1);
}
const myName = 'Bob';
`您好 ${myName} ,餐點已經準備好嚕!`
所謂的標籤樣板字面值,就是
showConsole`您好 ${myName} ,餐點已經準備好嚕!`;
直接將函式名稱 showConsole 接在樣板字面值之前,存檔執行以後如下

這樣的原理到底是怎麼運作的呢?
這邊解釋一下,我們傳入第一個參數 strings 是由樣板字面值拆解而來的,那麼拆解的規則就是依據我們在樣板字面值中的
${ } 作為分段點,所以可以看到這邊就被拆成了兩段,並且放入一個陣列中。
另外, arg 則是 ${ } 所傳入的變數,如果現在樣板字面值中的變數有多個的話,也會傳入多個。
// 標籤樣板字面值
function showConsole(strings, arg1, arg2, arg3) {
console.log(strings, arg1, arg2, arg3);
}
const myName = 'Bob';
showConsole`您好 ${myName} ,餐點已經準備好嚕! ${2} ${3}`;
眼尖的人可以發現,只要參數變多,我手動設定的 arg 變數也要跟著變多,但有時候就是不知道會傳幾個參數。
這個時候就要使用前面章節也使用過的其餘參數運算子 (...) ,來盛裝這些傳入的參數內容。
// 標籤樣板字面值
function showConsole(strings, ...arg) {
console.log(strings, arg);
}
const myName = 'Bob';
showConsole`您好 ${myName} ,餐點已經準備好嚕! ${2} ${3}`;
接下來我們就透過一個小範例來讓大家更了解要怎麼用運用標籤樣板字面值:
範例1
情境:我們現在希望得到一個組好的字串
`您好 <span> Bob </span> ,餐點已經準備好嚕~!`
那我們剛剛依據所學得,先做出第一部的程式碼來看看
const myName = 'Bob';
const sentence = `您好 ${myName} ,餐點已經準備好嚕!`;
console.log(sentence);

那麼我們要針對變數加上 span 的標籤的話,就要使用標籤樣板字面值,為此我們得先定義一個函式。
const myName = 'Bob';
const highlight = (strings, ...arg) =>(
strings.map((str, i) => `${str}`)
);
const sentence = highlight`您好 ${myName} ,餐點已經準備好嚕!`;
console.log(sentence);
定義好之後,我們針對 strings 進行 map 的迴圈,最後會回傳一個新的陣列。並且先把 str 印出來,
看看是不是被拆解的字串
如我們預期的,只有先是字串的陣列,那麼這個時候我們再使用 join,將陣列轉為字串,同時去除掉逗號。
const myName = 'Bob';
const highlight = (strings, ...arg) =>(
strings.map((str, i) => `${str}`).join('')
);
const sentence = highlight`您好 ${myName} ,餐點已經準備好嚕!`;
console.log(sentence);
到這邊已經準備得差不多,字串也組好了,接著下一步就是加入傳入的變數。
const myName = 'Bob';
const highlight = (strings, ...arg) =>(
strings.map((str, i) => `${str} <span>${arg[i]}</span>`).join('')
);
const sentence = highlight`您好 ${myName} ,餐點已經準備好嚕!`;
console.log(sentence);
只差一點點了,為什麼最後面會出現 undefined 呢?
原因是因為,strings 的陣列有兩個內容,但是 arg 的陣列只有一個參數傳入,所以當他找 arg[1] 的時候取不到值,
只好回傳 undefind
這個時候該怎麼辦呢?
這個時候我們就可以透過三元運算子來判斷這個值存不存在
const myName = 'Bob';
const highlight = (strings, ...arg) =>(
strings.map((str, i) => `${str} ${arg[i] ? `<span>${arg[i]}</span>` : '' }`).join('')
);
const sentence = highlight`您好 ${myName} ,餐點已經準備好嚕!`;
console.log(sentence);
這樣子就可以完成我們想要的結果囉!你可能會想說,為什麼需要這麼麻煩,一開始加上去不就好了嗎?
的確是這樣沒錯,但當變數一多的時候,直接套用就會比較方便喔!
範例2
現在我們是一個應用程式的開發者,現在有很多使用者會來傳入訊息。
<div id="message"></div>
const messageName = '小明';
document.querySelector('#message').innerHTML = `<p>${messageName}</p>傳來一則訊息!`
但是這樣的情況很容易造成 XSS 攻擊,簡單來說就是透過自定義的內容插入一些惡意的 js 程式,竊取用戶的個資或其他非開
發者預期的行為。
// const messageName = '小明';
const messageName = '<img onload="fetch(\'https://randomuser.me/api/\')" src="https://images.unsplash.com/photo-1587613842352-3022a317a088?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80" alt=""/>'
document.querySelector('#message').innerHTML = `<p>${messageName}</p>傳來一則訊息!`
原本應該是出現人名的地方卻傳來了一張圖片!
更可怕的是使用 onload 的屬性,有可能使用 ajax 或是把資料傳送給第三方的網站。
這個時候我們就可以使用標籤樣板字面值來把因為變數傳進來的有惡意的字串給換掉,防止 XSS 攻擊
function convertHTMLXSS(strings, ...keys) {
return strings.map((str, i) => (
`${str}${keys[i] ? `${keys[i]
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/')
}` : ''}`
)).join('');
}
const messageName = '<img onload="fetch(\'https://randomuser.me/api/\')" src="https://images.unsplash.com/photo-1587613842352-3022a317a088?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80" alt=""/>'
document.querySelector('#message').innerHTML = convertHTMLXSS`<p>${messageName}</p>傳來一則訊息!`