JavaScript – 字串操作(Strings)
主題:字串模板(Template Literals)
簡介
在 JavaScript 中,字串是最常見的資料型別之一,幾乎所有的 UI、日誌、API 請求都離不開字串的拼接與格式化。過去我們習慣使用 + 或 String.concat() 來組合字串,寫起來往往冗長且易於出錯。ES6(ECMAScript 2015)引入的 字串模板(Template Literals),不僅讓字串拼接變得直觀、可讀性更高,還支援多行字串、內嵌表達式、標籤函式等強大功能。
掌握模板字串後,你可以:
- 更快速地產生動態文字(如訊息提示、HTML 片段)
- 簡化多行文字的書寫(不再需要
\n或字串串接) - 利用標籤函式實作自訂的字串處理(如國際化、SQL 防注入)
本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,最後帶你看見模板字串在真實專案中的應用,幫助你在日常開發中即時上手。
核心概念
1. 基本語法:反引號與 ${}
模板字串使用 反引號(`) 包住,裡面的 ${ 表達式 } 會在執行時被求值並自動插入結果。
const name = 'Alice';
const age = 28;
// 傳統寫法
const msg1 = '我的名字是 ' + name + ',今年 ' + age + ' 歲。';
// 模板字串寫法
const msg2 = `我的名字是 ${name},今年 ${age} 歲。`;
console.log(msg1); // 我的名字是 Alice,今年 28 歲。
console.log(msg2); // 我的名字是 Alice,今年 28 歲。
重點:模板字串內部的
${}可以放入任意 JavaScript 表達式,甚至是函式呼叫或三元運算子。
2. 多行字串
使用普通字串(單/雙引號)時,需要手動插入 \n 或使用字串串接,既不直觀也不易維護。模板字串天然支援 多行,保留換行與空白。
const html = `
<div class="card">
<h2>${name}</h2>
<p>年齡:${age}</p>
</div>
`;
console.log(html);
輸出結果會完整保留換行與縮排,直接可作為 HTML 片段插入 DOM。
3. 內嵌表達式的彈性
${} 裡面不僅能放變數,還能放算術運算、函式呼叫、條件運算等:
const a = 5;
const b = 7;
const result = `計算結果:${a} + ${b} = ${a + b}`;
console.log(result); // 計算結果:5 + 7 = 12
// 三元運算子示例
const isAdult = age >= 18 ? '是' : '否';
const adultMsg = `我 ${age} 歲,是否已成年?${isAdult}`;
console.log(adultMsg);
4. 標籤模板(Tag Functions)
標籤函式允許在字串被解析之前先「攔截」它,對字串或插值做前處理。語法為 tagFunction\template`,其中 tagFunction` 會收到 字串陣列 與 插值值。
function escapeHTML(strings, ...values) {
// 將字串與插值交錯合併,並對插值做 HTML escape
const escaped = values.map(v => String(v)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
);
// 重建最終字串
return strings.reduce((acc, str, i) => acc + str + (escaped[i] || ''), '');
}
const userInput = `<script>alert('XSS');</script>`;
const safeHTML = escapeHTML`<p>使用者輸入:${userInput}</p>`;
console.log(safeHTML);
// <p>使用者輸入:<script>alert('XSS');</script></p>
應用:常用於 國際化(i18n)、SQL 防注入、自訂 DSL 等高階需求。
5. 內嵌函式與非同步運算
雖然模板字串本身是同步的,但你可以在 ${} 中呼叫返回 Promise 的函式,然後使用 await(需在 async 函式內):
async function fetchUser(id) {
const resp = await fetch(`https://api.example.com/users/${id}`);
return resp.json();
}
async function showUser(id) {
const user = await fetchUser(id);
const msg = `使用者名稱:${user.name},電子郵件:${user.email}`;
console.log(msg);
}
程式碼範例
以下提供 5 個實務範例,每個範例都配有註解說明,幫助你快速理解與應用。
範例 1:動態產生 API URL
function buildUrl(base, endpoint, params = {}) {
const query = new URLSearchParams(params).toString();
// 使用模板字串組合 URL
return `${base}/${endpoint}?${query}`;
}
// 呼叫
const url = buildUrl(
'https://api.example.com',
'search',
{ q: 'template literal', page: 2 }
);
console.log(url);
// https://api.example.com/search?q=template+literal&page=2
技巧:
URLSearchParams會自動編碼參數,避免手動encodeURIComponent。
範例 2:多行 HTML 產生器(適用於前端框架或純 DOM)
function renderCard(product) {
const { name, price, description } = product;
return `
<div class="product-card">
<h3>${name}</h3>
<p class="price">$${price.toFixed(2)}</p>
<p class="desc">${description}</p>
</div>
`.trim(); // 去除前後多餘的換行與空白
}
// 範例資料
const prod = {
name: '無線滑鼠',
price: 1299,
description: '藍牙 5.0,支援多裝置切換。'
};
document.body.innerHTML = renderCard(prod);
重點:使用
.trim()去除模板字串最外層的換行,避免產生不必要的空白節點。
範例 3:標籤函式實作簡易國際化(i18n)
const i18n = {
en: {
greeting: 'Hello, ${name}!',
items: 'You have ${count} items.'
},
zh: {
greeting: '哈囉,${name}!',
items: '你有 ${count} 件商品。'
}
};
function t(strings, ...values) {
// 假設目前語系是 zh(繁體中文)
const locale = 'zh';
const key = strings[0].trim(); // 取模板字串的第一段作為 i18n key
const template = i18n[locale][key];
// 用原始的 values 填入對應的模板
return new Function(...Object.keys(values[0] || {}), `return \`${template}\`;`)(...Object.values(values[0] || {}));
}
// 使用方式
const name = '小明';
const count = 3;
const msg = t`greeting ${{ name }}`;
const itemMsg = t`items ${ { count } }`;
console.log(msg); // 哈囉,小明!
console.log(itemMsg); // 你有 3 件商品。
說明:此範例示範如何把 模板字串本身 作為 i18n 鍵值,並在標籤函式內完成語系替換。實務上可配合
i18next、vue-i18n等套件。
範例 4:防止 XSS 的安全模板(結合範例 4 的 escapeHTML)
function safeHTML(strings, ...values) {
const escaped = values.map(v => String(v)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
);
return strings.reduce((acc, cur, i) => acc + cur + (escaped[i] ?? ''), '');
}
// 假設從表單取得使用者輸入
const userComment = `<img src=x onerror=alert('XSS')>`;
const output = safeHTML`<p>使用者留言:${userComment}</p>`;
document.body.innerHTML = output; // 安全的 HTML,不會觸發 XSS
實務觀點:在任何接受外部文字並直接插入 DOM 的情況,都應使用類似的 escape 機制。
範例 5:結合 await 的非同步模板(在 async 函式內)
async function getWeather(city) {
const resp = await fetch(`https://wttr.in/${city}?format=%C+%t`);
return resp.text(); // 例如回傳 "Sunny +25°C"
}
async function showWeather(city) {
const weather = await getWeather(city);
// 使用模板字串直接嵌入非同步結果
console.log(`現在 ${city} 的天氣是:${weather}`);
}
// 呼叫
showWeather('Taipei');
注意:模板字串本身不會「自動」等待 Promise,必須先
await取得結果再插入。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
| 忘記使用反引號 | 使用單/雙引號卻仍寫 ${},會被當成普通字串。 |
確認開頭與結尾都是 `,IDE 可設定語法高亮提醒。 |
| 多行字串前後多餘空白 | 模板字串保留所有換行與縮排,可能產生不必要的空白。 | 使用 .trim()、.trimStart() 或在模板字串前加 \ 來移除。 |
插值值為 undefined / null |
直接插入會得到字面文字 "undefined" 或 "null",可能造成 UI 錯誤。 |
在插值前做 預設值 或 條件判斷: ${value ?? ''}。 |
| 安全性問題(XSS) | 直接把使用者輸入放入模板字串並寫入 innerHTML。 |
使用 escapeHTML 標籤函式或 textContent;盡量避免直接插入 HTML。 |
| 大型模板的效能 | 每次執行模板字串都會重新建立字串,若在迴圈內大量使用可能影響效能。 | 先將不變的部分抽成常量,或使用 函式產生器(如 function render(item){...})減少重複運算。 |
標籤函式的 this 失效 |
標籤函式是普通函式,若使用 this 會是 undefined(嚴格模式)。 |
使用 箭頭函式 或明確傳入上下文,不依賴 this。 |
最佳實踐:
- 始終使用反引號:即使只有單行,也建議使用模板字串,保持一致性。
- 以變數或函式封裝模板:讓模板與資料的分離更清晰,便於單元測試。
- 使用標籤函式做安全或 i18n:避免在每個插值點手動處理。
- 在大型字串拼接時考慮
Array.join():對於極度大量的拼接(如報表生成),Array.join('')仍比模板字串更快。 - 保持可讀性:適度換行與縮排,讓同事容易閱讀與維護。
實際應用場景
前端 UI 組件
- 在 React、Vue、Svelte 等框架中,模板字串可快速產生 JSX/HTML 片段,特別是動態屬性(class、style)時。
- 範例:
const className = \btn btn-${type}`;`
日誌與錯誤訊息
- 結合
console.log,使用模板字串讓訊息更具結構:console.error(`資料庫連線失敗 (code: ${err.code}) - ${err.message}`);
- 結合
Email / Notification 模板
- 透過標籤函式或外部模板引擎(如 Handlebars)產生客製化信件內容,避免手動字串拼接。
國際化(i18n)
- 以模板字串作為 key,配合標籤函式在執行時自動切換語系,減少硬編碼。
SQL / NoSQL 查詢建構
- 使用模板字串結合參數化查詢,搭配標籤函式實作 防注入:
const query = sql`SELECT * FROM users WHERE id = ${userId}`;
- 使用模板字串結合參數化查詢,搭配標籤函式實作 防注入:
總結
- 模板字串(Template Literals) 是 ES6 之後最實用的字串工具,讓 插值、換行與自訂處理 變得自然且易讀。
- 只要掌握反引號、
${}以及標籤函式的概念,就能在 UI、API、日誌、國際化 等多種情境中大幅提升開發效率。 - 同時要注意 安全性(XSS)、空值處理 與 效能,遵守最佳實踐可避免常見陷阱。
透過本文的概念與範例,你已具備在日常開發中 自信使用模板字串 的能力。接下來不妨在自己的專案中挑選一個字串拼接的地方,改寫成模板字串,感受它帶來的可讀性與維護性的提升吧!祝你寫程式快樂、寫字串更快 🎉.