本文 AI 產出,尚未審核

JavaScript – 變數與資料型別(Variables & Data Types)

主題:字串(String)


簡介

在任何程式語言裡,字串都是最常見、也是最基礎的資料型別之一。無論是顯示使用者介面、處理 API 回傳的 JSON、還是產生動態的 HTML,幾乎每個步驟都離不開字串的操作。對於 JavaScript 初學者而言,掌握字串的基本概念與常用方法,等同於取得了「文字處理的瑞士刀」,能讓程式更具可讀性、可維護性,甚至提升效能。

然而,字串看似簡單,卻暗藏許多細節:Unicode 編碼、不可變性(immutable)、模板字串(template literals)等概念若不清楚,往往會在實作時踩到坑。本文將從 字串的宣告與基本操作 出發,逐步帶領讀者了解 JavaScript 中字串的全貌,並提供實務範例、常見陷阱與最佳實踐,協助你在日常開發中自信地使用字串。


核心概念

1. 字串的宣告與不可變性

在 JavaScript 中,字串是 原始值(primitive),使用單引號 (')、雙引號 (") 或反引號(`)皆可宣告。需要特別注意的是,字串是 不可變 的,也就是說對字串的任何「修改」操作,都會回傳一個 新字串,原本的字串不會被改變。

// 單引號、雙引號、模板字串皆可宣告
const single = 'Hello';
const double = "World";
const template = `Hello ${double}`; // 模板字串可直接插入變數

// 嘗試改變字串內容(不會改變原本的字串)
let greeting = 'Hello';
greeting[0] = 'h';      // ❌ 不會生效
console.log(greeting); // => 'Hello'

// 正確的做法是產生新字串
greeting = greeting.replace('H', 'h');
console.log(greeting); // => 'hello'

重點所有會改變字串內容的 API(如 replaceslicetoUpperCase 等)都不會改變原始字串,而是回傳新字串。


2. 常用字串方法

下面列出在日常開發中最常使用的幾個字串方法,並提供簡潔範例說明:

方法 功能 範例
length 取得字串長度(以 UTF-16 code unit 為單位) console.log('Hello'.length); // 5
charAt(index) 取得指定位置的字元 console.log('ABC'.charAt(1)); // B
includes(substr) 判斷是否包含子字串 console.log('JavaScript'.includes('Script')); // true
startsWith(substr) / endsWith(substr) 判斷開頭或結尾 console.log('test.js'.endsWith('.js')); // true
slice(start, end) 取出子字串(不改變原字串) console.log('abcdef'.slice(2,4)); // cd
substring(start, end) slice,但不接受負值 console.log('abcdef'.substring(2,4)); // cd
substr(start, length) 取出子字串,第二參數是長度 console.log('abcdef'.substr(2,3)); // cde
replace(search, replace) 替換第一個符合的子字串(支援正規表達式) console.log('foo bar'.replace('bar', 'baz')); // foo baz
trim() 移除兩端空白字元 console.log(' hello '.trim()); // 'hello'
toUpperCase() / toLowerCase() 大寫 / 小寫轉換 console.log('Js'.toUpperCase()); // JS
split(separator) 依分隔符切割成陣列 console.log('a,b,c'.split(',')); // ['a','b','c']
repeat(count) 重複字串 console.log('ha'.repeat(3)); // haha

範例 1:使用 splitmap 轉換 CSV 資料

const csv = "張三,25,台北\n李四,30,高雄\n王五,22,台中";
const rows = csv.split('\n');          // 以換行切割成每一列
const data = rows.map(line => line.split(',')); // 再以逗號切割欄位

console.log(data);
/*
[
  ['張三', '25', '台北'],
  ['李四', '30', '高雄'],
  ['王五', '22', '台中']
]
*/

範例 2:利用正規表達式一次取代多個子字串

const text = "2023-01-15, 2023/02/20, 2023.03.10";
const normalized = text.replace(/\d{4}[-\/\.](\d{2})[-\/\.](\d{2})/g, (match, m1, m2) => {
  // 轉成 ISO 格式 YYYY-MM-DD
  return `${match.slice(0,4)}-${m1}-${m2}`;
});

console.log(normalized);
// => "2023-01-15, 2023-02-20, 2023-03-10"

範例 3:模板字串與條件運算子結合產生動態訊息

function greet(name, isMorning) {
  return `早上好${isMorning ? '' : ','}${name}!`;
}

console.log(greet('小明', true));  // 早上好小明!
console.log(greet('小美', false)); // 早上好,小美!

範例 4:repeattrim 結合產生固定寬度的表格

function padRight(str, width) {
  const padding = ' '.repeat(Math.max(0, width - str.length));
  return (str + padding).trimEnd();
}

console.log(`|${padRight('商品', 10)}|${padRight('價格', 8)}|`);
console.log(`|${padRight('蘋果', 10)}|${padRight('$1.20', 8)}|`);
/*
|商品      |價格    |
|蘋果      |$1.20   |
*/

範例 5:toUpperCaseincludes 結合做不區分大小寫的搜尋

function containsIgnoreCase(source, keyword) {
  return source.toUpperCase().includes(keyword.toUpperCase());
}

console.log(containsIgnoreCase('JavaScript', 'script')); // true
console.log(containsIgnoreCase('Hello World', 'HELLO')); // true

3. Unicode、字元長度與 Emoji

JavaScript 的字串底層採用 UTF-16 編碼,這意味著 每個「code unit」佔兩個位元組。對於大多數拉丁字母與中、日、韓等常用字,長度計算與視覺上相同;但對於 代理配對(surrogate pair)(例如 Emoji、某些罕見漢字),一個「字元」實際上會佔用兩個 code unit,導致 length 會比預期大。

const smile = '😊'; // Emoji 使用 surrogate pair
console.log(smile.length); // 2,非 1
console.log([...smile].length); // 1,使用展開運算子把字串拆成真正的 Unicode 字元

若需要正確計算 Unicode 字元數,建議使用 展開運算子Array.from()String.prototype.codePointAt

const str = '𠮷野家';
console.log(str.length);          // 4(因為𠮷佔兩個 code unit)
console.log([...str].length);     // 3(正確的字元數)
console.log(Array.from(str).length); // 3

4. 模板字串(Template Literals)

模板字串是 ES6 引入的語法,使用反引號(`)包住,支援 插值(interpolation)多行文字、以及 標籤模板(tagged templates)。對於組合動態文字、產生 HTML 片段或 SQL 查詢字串非常方便。

const user = { name: '阿哲', age: 28 };
const html = `
  <div class="profile">
    <h2>${user.name}</h2>
    <p>年齡:${user.age} 歲</p>
  </div>
`;
console.log(html);

注意:模板字串內的換行與空白會原樣保留,若不想保留可使用 .trim() 或自行移除。


常見陷阱與最佳實踐

陷阱 說明 最佳做法
字串不可變 直接改變單一字元不會生效 透過 replaceslice 等產生新字串
length 與 Unicode Emoji、罕見漢字會算成 2 個長度 使用 Array.from(str).length 或展開運算子
== vs === == 會做型別轉換,可能導致意外結果 永遠使用嚴格相等 ===,字串比較亦如此
模板字串中的 XSS 直接插入使用者輸入的字串可能產生跨站腳本 先對輸入做 HTML escape,或使用安全的模板引擎
正則表達式的全局旗標 (g) replace 時若使用 /pattern/g,會一次取代所有匹配 確認是否真的需要全局取代,避免不小心改變多個位置
多重 + 連接 大量使用 + 會產生多個臨時字串,影響效能 使用模板字串或 Array.join('') 代替大量 +

小技巧

  1. 使用 String.prototype.padStart / padEnd 產生固定寬度的文字(如表格、編號)。
  2. 利用 Object.freeze 防止意外改變字串相關的常數物件。
  3. 在 Node.js 或瀏覽器端,若要處理大量文字建議使用 Buffer(Node)或 TextEncoder(瀏覽器)以減少記憶體分配。

實際應用場景

  1. 表單驗證

    • 使用 trim() 去除前後空白,includes() 或正則驗證 Email、電話等格式。
  2. 國際化(i18n)

    • 依照使用者語系切換字串,配合模板字串產生多語言訊息。
  3. 動態生成 HTML / JSX

    • 在 React、Vue 中,使用模板字串或字串插值產生 classNameid 等屬性。
  4. 日誌與除錯

    • 使用字串拼接或模板字串快速組合變數與訊息,方便記錄。
  5. API 請求的 URL 組合

    • 透過模板字串或 new URLSearchParams 組合查詢字串,確保編碼正確。
function buildApiUrl(base, params) {
  const query = new URLSearchParams(params).toString();
  return `${base}?${query}`;
}
console.log(buildApiUrl('https://api.example.com/users', { page: 2, limit: 20 }));
// => https://api.example.com/users?page=2&limit=20

總結

字串 在 JavaScript 中是 不可變的原始值,掌握其宣告方式、常用方法以及 Unicode 的細節,能讓你在處理文字、產生動態內容時更加得心應手。透過 模板字串正規表達式Array 方法 的結合,我們可以寫出簡潔、可讀且效能佳的程式碼。

在開發過程中,務必注意 字串長度與 Unicode 的差異、避免 XSS 風險、以及選擇適當的比較運算子。遵循本文的 最佳實踐,你將能在表單驗證、國際化、API 組合、前端渲染等多種情境下,靈活運用字串,提升專案的品質與維護效率。

關鍵提醒字串雖小,卻是程式碼中最常出現的資料型別之一,熟練它的使用,就是寫好程式的基礎。

祝你在 JavaScript 的世界裡,玩得開心、寫得順手!