本文 AI 產出,尚未審核

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, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
  );

  // 重建最終字串
  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>使用者輸入:&lt;script&gt;alert(&#39;XSS&#39;);&lt;/script&gt;</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 鍵值,並在標籤函式內完成語系替換。實務上可配合 i18nextvue-i18n 等套件。


範例 4:防止 XSS 的安全模板(結合範例 4 的 escapeHTML

function safeHTML(strings, ...values) {
  const escaped = values.map(v => String(v)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
  );
  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

最佳實踐

  1. 始終使用反引號:即使只有單行,也建議使用模板字串,保持一致性。
  2. 以變數或函式封裝模板:讓模板與資料的分離更清晰,便於單元測試。
  3. 使用標籤函式做安全或 i18n:避免在每個插值點手動處理。
  4. 在大型字串拼接時考慮 Array.join():對於極度大量的拼接(如報表生成),Array.join('') 仍比模板字串更快。
  5. 保持可讀性:適度換行與縮排,讓同事容易閱讀與維護。

實際應用場景

  1. 前端 UI 組件

    • 在 React、Vue、Svelte 等框架中,模板字串可快速產生 JSX/HTML 片段,特別是動態屬性(class、style)時。
    • 範例:const className = \btn btn-${type}`;`
  2. 日誌與錯誤訊息

    • 結合 console.log,使用模板字串讓訊息更具結構:
      console.error(`資料庫連線失敗 (code: ${err.code}) - ${err.message}`);
      
  3. Email / Notification 模板

    • 透過標籤函式或外部模板引擎(如 Handlebars)產生客製化信件內容,避免手動字串拼接。
  4. 國際化(i18n)

    • 以模板字串作為 key,配合標籤函式在執行時自動切換語系,減少硬編碼。
  5. SQL / NoSQL 查詢建構

    • 使用模板字串結合參數化查詢,搭配標籤函式實作 防注入
      const query = sql`SELECT * FROM users WHERE id = ${userId}`;
      

總結

  • 模板字串(Template Literals) 是 ES6 之後最實用的字串工具,讓 插值、換行與自訂處理 變得自然且易讀。
  • 只要掌握反引號、${} 以及標籤函式的概念,就能在 UI、API、日誌、國際化 等多種情境中大幅提升開發效率。
  • 同時要注意 安全性(XSS)空值處理效能,遵守最佳實踐可避免常見陷阱。

透過本文的概念與範例,你已具備在日常開發中 自信使用模板字串 的能力。接下來不妨在自己的專案中挑選一個字串拼接的地方,改寫成模板字串,感受它帶來的可讀性與維護性的提升吧!祝你寫程式快樂、寫字串更快 🎉.