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(如
replace、slice、toUpperCase等)都不會改變原始字串,而是回傳新字串。
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:使用 split 與 map 轉換 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:repeat 與 trim 結合產生固定寬度的表格
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:toUpperCase 與 includes 結合做不區分大小寫的搜尋
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() 或自行移除。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 最佳做法 |
|---|---|---|
| 字串不可變 | 直接改變單一字元不會生效 | 透過 replace、slice 等產生新字串 |
length 與 Unicode |
Emoji、罕見漢字會算成 2 個長度 | 使用 Array.from(str).length 或展開運算子 |
== vs === |
== 會做型別轉換,可能導致意外結果 |
永遠使用嚴格相等 ===,字串比較亦如此 |
| 模板字串中的 XSS | 直接插入使用者輸入的字串可能產生跨站腳本 | 先對輸入做 HTML escape,或使用安全的模板引擎 |
正則表達式的全局旗標 (g) |
replace 時若使用 /pattern/g,會一次取代所有匹配 |
確認是否真的需要全局取代,避免不小心改變多個位置 |
多重 + 連接 |
大量使用 + 會產生多個臨時字串,影響效能 |
使用模板字串或 Array.join('') 代替大量 + |
小技巧
- 使用
String.prototype.padStart/padEnd產生固定寬度的文字(如表格、編號)。 - 利用
Object.freeze防止意外改變字串相關的常數物件。 - 在 Node.js 或瀏覽器端,若要處理大量文字建議使用
Buffer(Node)或TextEncoder(瀏覽器)以減少記憶體分配。
實際應用場景
表單驗證
- 使用
trim()去除前後空白,includes()或正則驗證 Email、電話等格式。
- 使用
國際化(i18n)
- 依照使用者語系切換字串,配合模板字串產生多語言訊息。
動態生成 HTML / JSX
- 在 React、Vue 中,使用模板字串或字串插值產生
className、id等屬性。
- 在 React、Vue 中,使用模板字串或字串插值產生
日誌與除錯
- 使用字串拼接或模板字串快速組合變數與訊息,方便記錄。
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 的世界裡,玩得開心、寫得順手!