在 JavaScript 的世界里,字符串就像一位無處不在的 "語文老師"—— 從用戶昵稱到接口返回的 JSON,從 DOM 渲染到日志輸出,幾乎所有場景都離不開它。但就是這個最基礎的數(shù)據(jù)類型,曾讓無數(shù)開發(fā)者在 "拼接地獄" 里反復掙扎。
今天我們就來系統(tǒng)聊聊 JS 字符串的那些事兒:從最基礎的聲明方式,到 ES6 模板字符串帶來的革命,再到如何用字符串玩轉(zhuǎn)正則與 DOM 渲染。看完這篇,你對字符串的理解可能會提升一個維度。
一、字符串的兩種 "身份":原始值與對象
先看一個有意思的代碼片段:
let str = "hello"
let strObj = new String("hello")
console.log(typeof str)
console.log(typeof strObj)
console.log(str === strObj)
console.log(str.length === strObj.length)
同樣是 "hello",用不同方式聲明,居然有兩種完全不同的 "身份"。這背后藏著 JS 的 "包裝對象" 機制:
- 當我們用單引號、雙引號或反引號聲明字符串時,得到的是原始值(Primitive Value) ,這是最常用的形式
- 用
new String()創(chuàng)建的是字符串對象,它是 Object 類型的實例 - 當原始值調(diào)用字符串方法(比如
str.length)時,JS 會臨時創(chuàng)建包裝對象,調(diào)用完后立即銷毀
實際開發(fā)中幾乎不會用到new String(),因為原始值已經(jīng)能滿足 99% 的需求。但理解這種區(qū)別能幫我們避開很多坑,比如用typeof判斷類型時,對象類型會返回 "object",而原始值返回 "string"。
建議:統(tǒng)一用單引號或雙引號聲明字符串(團隊內(nèi)保持一致),避免混合使用導致風格混亂。
二、ES5 時代的 "拼接地獄":誰沒為字符串拼接過?
在 ES6 之前,處理復雜字符串拼接是件極其痛苦的事。比如我們要拼接一段用戶信息:
let user = { name: "張三", age: 28, isVip: true };
let info = "用戶信息:\n" +
"姓名:" + user.name + "\n" +
"年齡:" + user.age + "\n" +
"會員狀態(tài):" + (user.isVip ? "是" : "否");
這段代碼已經(jīng)算簡單的了,但依然有三個明顯的問題:
- 視覺混亂:
+號和引號交錯,稍長一點的字符串就像 "亂碼" - 換行麻煩:必須手動加
\n才能換行,HTML 字符串里還要處理<br> - 表達式嵌套痛苦:像
(user.isVip ? "是" : "否")這種表達式,一不小心就會漏掉括號
更可怕的是拼接 HTML 片段時:
// 拼接列表項的噩夢
let list = "<ul>"
for (let i = 0
list += "<li id='todo-" + todos[i].id + "'>" + todos[i].text + "</li>";
}
list += "</ul>";
這種代碼不僅難寫,還容易因為少寫一個引號或加號導致 bug,排查起來特別費勁。
三、ES6 模板字符串:字符串處理的 "救贖"
ES6 推出的模板字符串(Template String)徹底解決了這些問題,它用反引號(`)包裹字符串,支持兩種核心能力:變量嵌入與多行文本。
1. 變量嵌入:${} 里的魔法
模板字符串用${表達式}語法嵌入變量或計算結(jié)果,徹底告別+號:
let user = { name: "張三", age: 28, isVip: true };
let info = `用戶信息:
姓名:${user.name}
年齡:${user.age}
會員狀態(tài):${user.isVip ? "是" : "否"}`;
這里的${}里可以放任何 JS 表達式:變量、函數(shù)調(diào)用、三目運算甚至算術計算:
let a = 10, b = 20;
let calc = `計算結(jié)果:${a + b * 2}`;
function formatDate(date) {
return date.toLocaleDateString();
}
let log = `操作時間:${formatDate(new Date())}`;
這種寫法讓字符串與邏輯的結(jié)合變得無比自然。
2. 多行文本:直接換行的自由
模板字符串最直觀的優(yōu)勢是支持原生換行,不需要\n,寫 HTML 片段時簡直像在寫原生 HTML:
// 用模板字符串寫HTML
let todoItem = `
<li class="todo-item">
<span>${todo.text}</span>
<button data-id="${todo.id}">刪除</button>
</li>
`
對比 ES5 的拼接方式,這種寫法的可讀性提升了不止一個檔次。
四、實戰(zhàn):用模板字符串 + map 玩轉(zhuǎn) DOM 渲染
在前端開發(fā)中,我們經(jīng)常需要把數(shù)組數(shù)據(jù)渲染成 DOM 列表。結(jié)合數(shù)組的map方法和模板字符串,能讓這個過程變得異常優(yōu)雅。
先看一個完整案例:
// 數(shù)據(jù)源
const todos = [
{ id: 1, text: "學習ES6模板字符串" },
{ id: 2, text: "理解map方法的妙用" },
{ id: 3, text: "用模板字符串重構舊代碼" }
]
// 獲取容器
const todosEl = document.getElementById("todos")
// 渲染列表
todosEl.innerHTML = `
<ul class="todo-list">
${
todos.map(todo => `
<li data-id="${todo.id}">${todo.text}</li>
`).join("")
}
</ul>
`
這段代碼的精妙之處在于:
- map 遍歷數(shù)組:
todos.map()遍歷每個任務項,返回一個包含<li>字符串的新數(shù)組 - 模板字符串嵌套:在外部模板中嵌入
${},內(nèi)部再用模板字符串生成單個列表項 - join 消除逗號:
map返回的數(shù)組會自帶逗號分隔符,用join("")可以去除它們
如果用 ES5 實現(xiàn)同樣的功能,需要寫循環(huán)、累加字符串,代碼量至少增加一倍,而且可讀性差很多。
這里有個細節(jié):map的回調(diào)函數(shù)用了箭頭函數(shù),并且省略了return和大括號。因為箭頭函數(shù)在只有一個返回語句時可以簡寫,這與模板字符串配合使用,能讓代碼更緊湊。
五、模板字符串進階:標簽模板的黑科技
模板字符串還有一個高級用法:標簽模板(Tagged Templates) 。簡單說就是在模板字符串前加一個函數(shù)名,讓函數(shù)來處理模板內(nèi)容。
比如我們可以實現(xiàn)一個過濾 HTML 特殊字符的標簽函數(shù),防止 XSS 攻擊:
function safeHTML(strings, ...values) {
let result = "";
for (let i = 0; i < strings.length; i++) {
result += strings[i];
if (values[i]) {
result += String(values[i])
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """);
}
}
return result;
}
let userInput = '<script>alert("xss")</script>';
let html = safeHTML`<div>${userInput}</div>`;
console.log(html);
標簽模板的應用場景還有很多:格式化貨幣、國際化多語言、日志格式化等。它讓模板字符串從 "靜態(tài)拼接工具" 變成了 "可編程的字符串處理工具"。
六、字符串操作必備 API:讓處理更高效
除了模板字符串,JS 還提供了很多實用的字符串方法,配合模板字符串使用能大幅提升效率:
includes() :判斷字符串是否包含某子串
let str = "hello world";
if (str.includes("world")) {
console.log("包含目標子串");
}
startsWith()/endsWith() :判斷字符串開頭 / 結(jié)尾
let filename = "report.pdf";
if (filename.endsWith(".pdf")) {
console.log("是PDF文件");
}
padStart()/padEnd() :補全字符串長度(常用于格式化)
// 格式化時間:將"9"變成"09"
let minute = "9";
console.log(minute.padStart(2, "0")); // "09"
split() + join() :字符串替換或切割
// 替換所有空格為下劃線
let str = "hello world js";
console.log(str.split(" ").join("_")); // "hello_world_js"
這些方法與模板字符串結(jié)合,能解決大部分字符串處理場景。比如格式化日期:
function formatTime(seconds) {
let min = Math.floor(seconds / 60).toString().padStart(2, "0");
let sec = (seconds % 60).toString().padStart(2, "0");
return `${min}:${sec}`;
}
console.log(formatTime(65));
七、避坑指南:這些字符串陷阱要注意
原始值與對象的區(qū)別:永遠不要用new String()創(chuàng)建字符串,它會導致typeof判斷異常,還可能引發(fā)全等比較錯誤("a" === new String("a")為 false)
模板字符串的空格問題:模板字符串會保留換行和縮進的空格,在渲染 HTML 時可能導致意外的空白,必要時可以用trim()處理:
let str = `
hello
`.trim()
**中的表達式副作用:如果{}` 里的表達式有副作用(比如修改變量、調(diào)用接口),可能會導致不可預期的結(jié)果,盡量保持表達式純凈:
// 不推薦:表達式有副作用
let count = 0
let str = `當前計數(shù):${count++}`
性能考量:頻繁拼接超長字符串時,模板字符串的性能略遜于數(shù)組push+join,但現(xiàn)代 JS 引擎已經(jīng)優(yōu)化得很好,除非是極端場景(比如拼接 10 萬 + 字符),否則不必過度關注。
結(jié)語:字符串處理的 "優(yōu)雅之道"
從+號拼接的繁瑣,到模板字符串的優(yōu)雅,JS 字符串的發(fā)展其實映射了前端開發(fā)的進化史 —— 從關注 "能不能實現(xiàn)",到追求 "如何更優(yōu)雅地實現(xiàn)"。
掌握模板字符串的用法,不僅能減少 80% 的字符串拼接代碼,還能讓邏輯與 UI 描述更緊密地結(jié)合。而理解字符串的原始值與對象特性、熟練使用字符串 API,則能幫我們避開很多隱藏的坑。
?轉(zhuǎn)自https://juejin.cn/post/7567220483964944430
該文章在 2025/11/3 14:48:50 編輯過