讓開發者生活更輕松的 JavaScript 字符串方法
JavaScript 最初被引入作為一種簡單的客戶端腳本語言,但現在,它已經成為一種真正的 WORA(一次編寫到處運行)語言,使開發者能夠構建桌面、移動、電視、CLI 和嵌入式應用程序。JavaScript 的初學者友好語法、生產性語言特性和良好管理的 ECMAScript 規范,激勵了所有人使用 JavaScript 進行通用編程。
ECMAScript 標準通過提供許多語言特性來提高開發者的生產力。它通過內置的 String 對象引入了幾種字符串方法,使開發者能夠高效地處理字符串數據。這些高效的字符串方法激勵開發者使用 JavaScript 解決文本處理問題,而無需使用外部庫或從頭編寫冗長的代碼。
在本教程中,將解釋必須了解的 JavaScript 字符串處理方法,讓你能夠編寫干凈、自解釋的代碼。
使用 at() 方法進行簡潔的負索引訪問
JavaScript 在字符串和數組對象中實現了傳統的基于方括號的索引元素訪問,但由于語言設計的限制,它不像 Python 那樣實現負索引支持。例如,下面代碼片段中的第二條日志語句返回 undefined,因為它嘗試查找的是對象屬性而不是索引元素:
let m = 'JavaScript';
console.log(m[m.length - 1]); // t
console.log(m[-1]); // undefined
可以使用 JavaScript 的 Proxy 對象實現字符串的負索引支持,但這不如在字符串和數組對象中實現的內置 at()
方法高效。at()
方法讓我們可以簡化最后一個字符的訪問,如下所示:
let m = 'JavaScript';
console.log(m.at(-1)); // t
使用 includes()、startsWith() 和 endsWith() 字符串搜索方法
過去,開發者經常使用 indexOf() 方法在字符串對象中搜索字符串片段。他們使用 Regex 或基于 indexOf()/substring() 的解決方案來檢查特定字符串的開始和結束。ES6 版本為這些需求引入了單獨的內置字符串方法。
includes() 方法檢查字符串中是否存在特定字符集:
let m = 'JavaScript';
console.log(m.includes('Java')); // true
startsWith() 方法檢查字符串的開頭,如下代碼片段所示:
let filename = '_testmatrix.json';
console.log(filename.startsWith('_')); // true
同時,endsWith() 方法檢查字符串的結尾,如下所示:
let filename = '_testmatrix.json';
console.log(filename.endsWith('.json')); // true
這些內置字符串方法讓我們可以編寫簡單的語句來滿足常規的字符串搜索需求,而無需使用基于正則表達式或其他算法的解決方案。
使用 repeat() 方法進行字符串重復
在通用編程語言中構建字符串時,我們經常需要進行字符串重復。假設需要使用 ASCII 字符在終端上創建一條水平線。Python 允許開發者使用 *
操作符高效地處理這個問題,如下代碼片段所示:
print('+-' * 10) # +-+-+-+-+-+-+-+-+-+-
在 ES6 之前,JavaScript 開發者不得不使用一些技巧在不使用循環結構的情況下重復字符串。大多數開發者使用以下方法:
console.log(new Array(11).join('+-')); // +-+-+-+-+-+-+-+-+-+-
ES6 引入了 repeat() 字符串方法,以高效地替代舊的非自解釋代碼進行字符串重復:
console.log('+-'.repeat(10));
使用字符串修剪方法刪除多余的空白
在各種開發場景中,我們經常需要通過刪除空白字符來預處理字符串。例如,您可能需要刪除使用 <textarea> HTML 元素捕獲的用戶輸入中的空白。過去,大多數開發者使用正則表達式來清理字符串,如下所示:
function trim(str) {
return str.replace(/^\s+|\s+$/g, '');
}
console.log(trim(' Hello JavaScript ')); // 'Hello JavaScript'
上述 trim() 函數刪除了前導和尾隨的空白字符。
JavaScript 在 String 對象上實現了 trim()、trimStart() 和 trimEnd() 方法,用于處理空白刪除。trim() 方法刪除前導和尾隨的空白字符。同時,其他兩個方法幫助我們有選擇地清理前導和尾隨空白,如下代碼片段所示:
let txt = ' Hello JavaScript \n ';
console.log(txt.trimStart()); // 'Hello JavaScript \n '
console.log(txt.trimEnd()); // ' Hello JavaScript'
console.log(txt.trim()); // 'Hello JavaScript'
使用 String.raw() 靜態方法創建原始字符串
JavaScript 提供了模板字符串特性,以高效地生成包含各種表達式的動態字符串。此特性消除了使用 ${} 語法進行字符串拼接的需求。JavaScript 允許你使用標記函數實現自定義模板字面量處理器。
看以下示例代碼片段:
function secret(strings, ...exps) {
return strings.reduce((acc, str, i) =>
(acc + str + (exps.length > i ? '*'.repeat(exps[i].toString().length) : '')), '');
}
let txt = secret`My username is ${'Bingo'}, and my password is ${1234}`;
console.log(txt); // My username is *****, and my password is ****
上述 secret() 標記函數為模板字符串表達式中的所有值添加星號字符。正如你已經注意到的,我們可以通過在特定模板字符串前使用標記函數,而不是使用傳統的括號式函數調用語法來執行標記函數。
內置的 String.raw() 標記函數允許我們存儲不處理轉義字符的原始字符串。假設需要使用 JavaScript 存儲以下 Windows 文件路徑:
C:\Projects\MyProject1\myproject.config.json
我們不能正確地在 JavaScript 中存儲此字符串,因為它的轉義字符被處理并刪除了多個字符:
let path = 'C:\Projects\MyProject1\myproject.config.json';
console.log(path); // C:ProjectsMyProject1myproject.config.json
在這里,我們可以使用 String.raw() 標記函數來防止轉義字符處理:
let path = String.raw`C:\Projects\MyProject1\myproject.config.json`;
console.log(path); // C:\Projects\MyProject1\myproject.config.json
String.raw 標記函數自動為轉義字符添加雙反斜杠,以正確存儲原始字符串。此標記函數還適用于存儲包含字符串中反斜杠的正則表達式定義:
let regex = String.raw`\s*${10}\s*`;
console.log('2 10 20'.replace(new RegExp(regex), '')); // 220
在上面的示例中,我們在字符串中存儲了動態構建的正則表達式定義,而無需使用雙反斜杠。
使用 padStart() 和 padEnd() 方法填充字符串
在構建 Web 應用程序時,填充字符串是一個常見需求。我們經常需要為字符串應用填充字符,以獲得固定的字符串長度。假設有一個從 0 開始并以 10 結束的數字列表,并在表格列中顯示。我們可以使用前導零填充這一場景,通過一個共享的實用函數來提高表格的視覺效果,如下所示:
function format(num) {
return num.toString().padStart(2, '0');
}
let arr = new Array(11).fill().map((e, i) => i);
document.write(`<table>
<tr>
<th>#</th>
</tr>
`);
for(let n of arr) {
document.write(`<tr>
<td>${format(n)}</td>
</tr>`);
}
document.write('</table>');
在這里,我們使用內置的 padStart() 方法添加前導零填充,而不是編寫自己的字符串填充算法。上述代碼片段渲染了零填充的數字,如下所示:
ECMAScript 標準還引入了 padEnd() 方法,用于在特定字符串末尾添加填充字符,具體如下:
let token = 'TK023550L';
let displayToken = token.substring(0, 5).padEnd(token.length, '*');
console.log(`Token: ${displayToken}`); // Token: TK023****
使用 replaceAll() 和 matchAll() 方法進行高效字符串處理
在 ES2021 之前,開發者不得不使用正則表達式來替換所有出現的特定字符串片段,因為 replace() 函數只替換第一次出現:
let msg = 'Hello JavaScript, Hello JavaScript';
console.log(msg.replace('JavaScript', 'Js')); // Hello Js, Hello JavaScript
console.log(msg.replace(/JavaScript/g, 'Js')); // Hello Js, Hello Js
ES2021 引入了 replaceAll() 字符串方法,以替換所有出現的特定字符串片段:
console.log(msg.replaceAll('JavaScript', 'Js')); // Hello Js, Hello Js
同時,ES2020 版本引入的 matchAll() 方法幫助我們使用可迭代協議遍歷正則表達式匹配。此方法幫助我們避免使用 while 循環和 Regex.exec() 的傳統正則表達式結果迭代方法(請參閱此解釋)。
看以下代碼片段:
let msg = 'AT01 BT023 AB02 AT224';
let matches = msg.matchAll(/\b(([A-Z]{2})([0-9]{2}))\b/g);
for(let match of matches) {
console.log(`${match[2]}-${match[3]}`); // AT-01, AB-02
}
上述代碼片段使用 matchAll() 方法遍歷所有捕獲組。