`String` 物件

`String` 物件

本文將說明 String 物件。

說明從基礎到進階技術都包含,內容包含與 Unicode 和正規表達式相關的陷阱,並以易於理解的方式逐步解說。

YouTube Video

String 物件

在 JavaScript 開發中,字串是日常開發中最常用的型別之一。

基本型字串與字串物件的差異

基本型字串(如 "hello")與包裝物件(如 new String("hello"))的行為不同。通常應該使用基本型,幾乎沒有需要用物件型的情況。

1// Primitive string
2const a = "hello";
3
4// String wrapper object
5const b = new String("hello");
6
7console.log(typeof a); // "string"
8console.log(typeof b); // "object"
9console.log(a === b);  // false — wrapper objects are not strictly equal
  • 這段程式碼顯示了基本型與包裝型在型別上的差異,以及它們在嚴格比較時的行為。大多數情況下,請避免使用 new String(),僅用基本型即可。

建立字串的方法(字面值與樣板字串)

樣板字串便於嵌入變數並撰寫多行字串。可以直覺地插入變數並計算表達式。

1const name = "Alice";
2const age = 30;
3
4// Template literal
5const greeting = `Name: ${name}, Age: ${age + 1}`;
6
7console.log(greeting); // "Name: Alice, Age: 31"
  • 樣板字串可讀性高,非常適合建立複雜或多行字串。

常用方法(搜尋與擷取子字串)

String 物件具有許多基本方法。

 1const text = "Hello, world! Hello again.";
 2
 3// search
 4console.log(text.indexOf("Hello"));       // 0
 5console.log(text.indexOf("Hello", 1));    // 13
 6console.log(text.includes("world"));      // true
 7console.log(text.startsWith("Hello"));    // true
 8console.log(text.endsWith("again."));     // true
 9
10// slice / substring
11console.log(text.slice(7, 12));           // "world"
12console.log(text.substring(7, 12));       // "world"
  • slicesubstring 類似,但對負索引的處理方式不同。slice 會將負值解讀為從字串尾端起算的位置。請明確選擇要使用哪一個方法。

分割與合併(split / join)

將字串拆分為陣列進行處理,之後再合併回來是很常見的做法。

1const csv = "red,green,blue";
2const arr = csv.split(","); // ["red","green","blue"]
3
4console.log(arr);
5console.log(arr.join(" | ")); // "red | green | blue"
  • 常見的流程是先用 split 拆分字串,再用 mapfilter 處理陣列,最後用 join 合併回字串。

取代與正則表達式

replace 預設只會取代第一個匹配項目。如果要取代所有匹配,需配合正規表達式 g 標誌。若將函數作為替換項目傳入,亦可進行動態取代。

 1const s = "foo 1 foo 2";
 2
 3// replace first only
 4console.log(s.replace("foo", "bar")); // "bar 1 foo 2"
 5
 6// replace all using regex
 7console.log(s.replace(/foo/g, "bar")); // "bar 1 bar 2"
 8
 9// replace with function
10const r = s.replace(/\d+/g, (match) => String(Number(match) * 10));
11console.log(r);    // "foo 10 foo 20"
  • 利用函數進行動態替換時,可以簡潔地寫出解析與轉換匹配內容的程式碼。

大小寫轉換與標準化

為了支援多語言與比較,除了 toLowerCasetoUpperCase 之外,Unicode 正規化(normalize)也很重要。特別是在比較帶有重音的字元時尤其需要。

 1// Case conversion example:
 2// "\u00DF" represents the German letter "ß".
 3// In some locales, converting "ß" to uppercase becomes "SS".
 4// JavaScript follows this behavior.
 5console.log("\u00DF");
 6console.log("\u00DF".toUpperCase()); // "SS"
 7
 8// Unicode normalization example:
 9// "e\u0301" is "e" + a combining acute accent.
10// "\u00e9" is the precomposed character "é".
11// These two look the same but are different code point sequences.
12const a = "e\u0301";
13const b = "\u00e9";
14
15console.log(a === b);   // false: different underlying code points
16console.log(a.normalize() === b.normalize()); // true: normalized to the same form
  • 不同的 Unicode 表示方式(如連字、組合字元),比對時可能不相等,因此比較前應先用 normalize() 標準化。

Unicode 與碼點(代理對的處理)

JavaScript 的字串是由 UTF-16 編碼單元組成,因此有些字元(如表情符號)一個字會佔用兩個編碼單元。若要以實際字元單位處理,請使用 Array.from、展開運算符或 for...of

 1// Emoji composed with multiple code points:
 2// "\u{1F469}" = woman, "\u{200D}" = Zero Width Joiner (ZWJ),
 3// "\u{1F4BB}" = laptop. Combined, they form a single emoji: 👩‍💻
 4const s = "\u{1F469}\u{200D}\u{1F4BB}";
 5console.log(s);
 6
 7// Length in UTF-16 code units (not actual Unicode characters):
 8// Because this emoji uses surrogate pairs + ZWJ, the length may be > 1.
 9console.log("Length:", s.length);
10
11// Iterate by Unicode code points (ES6 for...of iterates code points):
12// Each iteration gives a full Unicode character, not UTF-16 units.
13for (const ch of s) {
14  console.log(ch);
15}
16
17// Convert to an array of Unicode characters:
18console.log(Array.from(s));
  • length 是回傳編碼單元數量,對於表情符號或連字,結果可能與預期數量不同。for...ofArray.from 處理的單位接近顯示字元(字位群),若需要完整支援請考慮使用專門的函式庫。

正規表達式下的安全替換(處理用戶輸入時)

如果您在將使用者輸入嵌入正規表達式時忘記跳脫,可能會導致不可預期的行為和漏洞。在輸入內容用於模式之前,一定要先進行跳脫處理。

1function escapeRegex(s) {
2  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3}
4
5const userInput = "a+b";
6const pattern = new RegExp(escapeRegex(userInput), "g");
7console.log("a+b a+b".replace(pattern, "X")); // "X X"
  • 切勿將用戶字串直接用於正規表達式;在產生正規表達式前務必先跳脫。

效能建議:串接與樣板字串

大量串接字串時,先收集到陣列再用 join 合併,效能更佳。另一方面,樣板字串可讀性高並且在大多數情況下速度也很快。

 1// concatenation in loop (less ideal)
 2let s = "";
 3for (let i = 0; i < 1000; i++) {
 4  s += i + ",";
 5}
 6
 7// using array + join (often faster for many pieces)
 8const parts = [];
 9for (let i = 0; i < 1000; i++) {
10  parts.push(i + ",");
11}
12const s2 = parts.join("");
  • 現代 JavaScript 引擎優化良好,少量串接時通常不需擔心效能。但如果需要串接數萬次以上,使用 join 會更有效率。

實用技巧:補齊、去頭尾空白與重複

trimpadStartpadEndrepeat 是非常方便的方法,在日常的字串處理中特別有用。它們經常用於實際情境,例如格式化輸入值或標準化輸出格式。

1console.log("  hello  ".trim());       // "hello"
2console.log("5".padStart(3, "0"));     // "005"
3console.log("x".repeat(5));            // "xxxxx"
  • 這些方法可用於表單輸入的規範化或生成固定寬度的輸出。

字串比對(區域性比對)

localeCompare 非常適合以字典順序針對不同語言進行字串比對。你可以指定語言和敏感度選項(如區分大小寫等)。

1console.log(
2  "\u00E4".localeCompare("z", "de")
3); // may be -1 or other depending on locale
4
5console.log(
6  "a".localeCompare("A", undefined, { sensitivity: "base" })
7); // 0
  • 如需國際化比對,請使用 localeCompare 並指定正確的地區與選項。

實用範例:將一行 CSV 轉換為物件(實務流程)

常見作法是以 splittrimmap 等組合將一行 CSV 解析成物件。若需要處理帶引號欄位或複雜 CSV,請使用專門的 CSV 解析器。

 1// simple CSV parse (no quotes handling)
 2function parseCsvLine(line, headers) {
 3  const values = line.split(",").map(v => v.trim());
 4  const obj = {};
 5  headers.forEach((h, i) => obj[h] = values[i] ?? null);
 6  return obj;
 7}
 8
 9const headers = ["name", "age", "city"];
10const line = " Alice , 30 , New York ";
11console.log(parseCsvLine(line, headers));
12// { name: "Alice", age: "30", city: "New York" }
  • 這個方法適用於簡單的 CSV,但無法處理引號內有逗號的狀況。

常見陷阱

JavaScript 字串處理中有一些容易忽略的規範與行為。為避免意外錯誤,請特別注意以下幾點。

  • 使用 new String() 可能導致型別判斷或比對結果錯誤。絕大多數情況下,基本型字串已經足夠。
  • 在 Unicode 中,一個可見字元可能包含多個編碼單元。因此 length 回傳值不一定等於實際顯示的字元數量。
  • 將用戶輸入用於正規表達式時,請務必先跳脫。
  • String.prototype.replace() 預設只取代第一個符合的字串。如果您想要替換所有出現的項目,請在您的正規表達式中使用 /g 標誌。
  • 字串是不可變的,所以每次操作會回傳新字串。務必記得將回傳結果接收下來。

總結

雖然 JavaScript 字串看似簡單,但 Unicode 與不可變等特性務必理解清楚。只要掌握基礎,就能大幅提升字串處理的可靠性與可讀性。

您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。

YouTube Video