字符串详解
字符串是 JavaScript 中使用最频繁的数据类型之一,文本处理、表单校验、模板渲染都离不开它。本文将系统讲解字符串的创建、不可变性、常用方法、模板字符串、Unicode 细节以及正则表达式基础,帮助读者完整掌握字符串操作。
一、字符串的创建与不可变性
1.1 创建方式
字符串可以用单引号、双引号或反引号创建:
javascript
const s1 = "双引号";
const s2 = '单引号';
const s3 = `反引号(模板字符串)`;
// 转义字符
const s4 = "他说:\"你好\"";
const s5 = '换行符:\n下一行';
const s6 = "反斜杠:\\";| 引号 | 特点 | 适用场景 |
|---|---|---|
'' | 单引号,无插值 | 大多数普通字符串 |
"" | 双引号,无插值 | 内容含单引号时 |
`` | 反引号,支持插值与换行 | 模板字符串 |
1.2 不可变性
字符串是原始类型,一旦创建就不可修改。所有"修改"字符串的方法实际上都返回新字符串,原字符串保持不变:
javascript
let str = "hello";
str.toUpperCase();
console.log(str); // hello,原字符串未变
str = str.toUpperCase();
console.log(str); // HELLO,需要重新赋值这一特性也决定了频繁拼接字符串可能产生性能开销,批量拼接时建议使用数组 join。
二、字符访问与 length
javascript
const str = "JavaScript";
console.log(str.length); // 10
console.log(str[0]); // J,按索引访问
console.log(str[str.length - 1]); // t,最后一个字符
console.log(str.at(-1)); // t,ES2022 支持负索引
console.log(str.charAt(4)); // S
// 越界访问
console.log(str[100]); // undefined
console.log(str.charAt(100)); // "",空字符串三、常用方法大全
3.1 大小写转换
javascript
console.log("hello World".toUpperCase()); // HELLO WORLD
console.log("Hello World".toLowerCase()); // hello world3.2 包含判断
javascript
const str = "JavaScript 字符串教程";
console.log(str.includes("字符串")); // true
console.log(str.includes("Java", 5)); // false,从下标 5 开始查找
console.log(str.startsWith("Java")); // true
console.log(str.endsWith("教程")); // true
console.log(str.startsWith("Script", 4)); // true,指定开始位置3.3 截取:slice / substring / substr 对比
| 方法 | 参数含义 | 负参数 | 特例 |
|---|---|---|---|
slice(start, end) | 含头不含尾 | 支持 | 参数互换无意义,start > end 返回空串 |
substring(start, end) | 含头不含尾 | 视为 0 | 自动交换大小参数 |
substr(start, length) | 起始位置 + 长度 | 支持负 start | 已被弃用,不推荐 |
javascript
const s = "abcdef";
console.log(s.slice(1, 4)); // bcd
console.log(s.slice(-3)); // def
console.log(s.slice(2, -1)); // cde
console.log(s.substring(1, 4)); // bcd
console.log(s.substring(4, 1)); // bcd,参数自动交换
console.log(s.substring(-2, 3)); // abc,负数视为 0
console.log(s.substr(1, 3)); // bcd(已弃用)3.4 拆分与合并
javascript
const csv = "苹果,香蕉,橙子";
console.log(csv.split(",")); // ['苹果','香蕉','橙子']
console.log(csv.split(",", 2)); // ['苹果','香蕉'],限制数量
console.log("abc".split("")); // ['a','b','c']
console.log("a-b-c".split("-")); // ['a','b','c']
console.log(["2026", "08", "09"].join("-")); // 2026-08-09
console.log(["a", "b"].join("")); // ab3.5 去除空白
javascript
const text = " 前后有空格 ";
console.log(text.trim()); // "前后有空格"
console.log(" 左".trimStart()); // "左"
console.log("右 ".trimEnd()); // "右"3.6 替换
javascript
console.log("2026-08-09".replace("-", "/")); // 2026/08-09,只替换第一处
console.log("2026-08-09".replaceAll("-", "/")); // 2026/08/09,全部替换
// replace 结合正则实现全局替换
console.log("a-b-c".replace(/-/g, "_")); // a_b_c
// 替换函数
console.log(
"hello world".replace(/o/g, (match) => match.toUpperCase())
); // hellO wOrld3.7 填充与重复
javascript
console.log("7".padStart(2, "0")); // 07
console.log("2026-8-9".padStart(10, "0")); // 002026-8-9
console.log("hello".padEnd(8, "!")); // hello!!!
console.log("ab".repeat(3)); // ababab
console.log("*".repeat(10)); // **********3.8 查找位置
javascript
const str = "JavaScript 是 JS 语言";
console.log(str.indexOf("JS")); // 13
console.log(str.indexOf("JS", 15)); // -1,从下标 15 开始找不到
console.log(str.lastIndexOf("a")); // 3
console.log(str.search("语言")); // 163.9 字符编码相关
javascript
console.log("A".charCodeAt(0)); // 65,UTF-16 码元
console.log("中".charCodeAt(0)); // 20013
console.log(String.fromCharCode(65)); // A
// codePointAt 能正确处理代理对(如 emoji)
console.log("😀".charCodeAt(0)); // 55357,高位代理,不是完整码点
console.log("😀".codePointAt(0)); // 128512,完整码点
console.log(String.fromCodePoint(128512)); // 😀四、模板字符串
4.1 插值
javascript
const name = "张三";
const age = 30;
console.log(`${name} 今年 ${age} 岁`); // 张三 今年 30 岁
console.log(`明年 ${age + 1} 岁`); // 表达式可计算4.2 多行文本
javascript
const poem = `
床前明月光,
疑是地上霜。
`;
console.log(poem);
// 输出保留换行4.3 嵌套使用
javascript
const isVIP = true;
const greeting = `欢迎${isVIP ? "尊贵的" : ""}用户`;
console.log(greeting); // 欢迎尊贵的用户4.4 标签模板
标签模板允许用一个函数处理模板字符串,先拿到字面量片段与插值表达式,再按需重组:
javascript
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return result + str + (values[i] ? `<b>${values[i]}</b>` : "");
}, "");
}
const user = "李四";
const score = 98;
console.log(highlight`用户 ${user} 得分 ${score} 分`);
// 用户 <b>李四</b> 得分 <b>98</b> 分五、字符串与 Unicode
5.1 码点与代理对
UTF-16 以 16 位码元(code unit)为单位存储,基本平面字符(BMP)占 1 个码元,而 emoji 等增补平面字符需要 2 个码元组成的代理对:
javascript
const emoji = "😀";
console.log(emoji.length); // 2,长度按码元计算
console.log(emoji[0]); // 半个代理,显示为乱码符号
console.log(emoji.codePointAt(0).toString(16)); // 1f6005.2 用 for-of 按码点遍历
for-of 按完整码点遍历,是处理 emoji 等字符的正确方式:
javascript
for (const ch of "a😀中") {
console.log(ch); // a、😀、中,逐个输出
}
// 对比:for 循环按码元遍历会拆散代理对
for (let i = 0; i < "a😀中".length; i++) {
console.log("a😀中"[i]); // a、半个代理、半个代理、中
}
// 展开运算符也按码点工作
console.log([..."a😀中"].length); // 3六、正则表达式基础
正则表达式(RegExp)用于模式匹配,是字符串处理的重要工具。
6.1 创建方式
javascript
// 字面量方式(推荐)
const re1 = /abc/;
// 构造函数方式,可用于动态拼接
const pattern = "abc";
const re2 = new RegExp(pattern, "g");6.2 匹配方法
| 方法 | 用途 | 返回值 |
|---|---|---|
str.match(re) | 字符串侧匹配 | 匹配结果数组或 null |
str.matchAll(re) | 全局匹配(需 g) | 迭代器 |
re.test(str) | 判断是否匹配 | 布尔值 |
re.exec(str) | 正则侧匹配 | 匹配详情或 null |
javascript
const str = "手机号 13800138000,座机 010-12345678";
const phone = /1[3-9]\d{9}/;
console.log(phone.test(str)); // true
console.log(phone.exec(str)[0]); // 13800138000
const all = str.match(/\d+/g);
console.log(all); // ['13800138000','010','12345678']
// matchAll 配合展开
const iter = str.matchAll(/\d+/g);
console.log([...iter].map((m) => m[0])); // ['13800138000','010','12345678']6.3 字符类与量词
| 语法 | 含义 | 语法 | 含义 |
|---|---|---|---|
\d | 数字 | \D | 非数字 |
\w | 字母数字下划线 | \W | 非单词字符 |
\s | 空白 | \S | 非空白 |
. | 任意字符(换行除外) | [abc] | 字符集 |
[a-z] | 范围 | [^0-9] | 取反 |
| 量词 | 含义 | 示例 |
|---|---|---|
* | 0 次或多次 | ab* 匹配 a、ab、abb |
+ | 1 次或多次 | ab+ 匹配 ab、abb |
? | 0 次或 1 次 | ab? 匹配 a、ab |
{n} | 恰好 n 次 | \d{3} |
{n,} | 至少 n 次 | \d{3,} |
{n,m} | n 到 m 次 | \d{3,5} |
6.4 分组与修饰符
javascript
// 分组:用括号捕获
const date = "2026-08-09";
const re = /(\d{4})-(\d{2})-(\d{2})/;
const result = date.match(re);
console.log(result[1]); // 2026
console.log(result[2]); // 08
console.log(result[3]); // 09
// 命名分组(ES2018)
const reNamed = /(?<year>\d{4})-(?<month>\d{2})/;
console.log(date.match(reNamed).groups.month); // 08| 修饰符 | 含义 |
|---|---|
g | 全局匹配,查找所有结果 |
i | 忽略大小写 |
m | 多行模式,^/$ 匹配每行首尾 |
s | 点号匹配所有字符(包括换行) |
javascript
console.log(/hello/i.test("HELLO")); // true
console.log("abc\ndef".match(/^def/m)); // ['def'],m 模式下匹配第二行
console.log(/a.b/s.test("a\nb")); // true,s 模式下点号匹配换行6.5 常见校验示例
javascript
// 邮箱校验
const emailRe = /^[\w.-]+@[\w-]+(\.[\w-]+)+$/;
console.log(emailRe.test("user@example.com")); // true
console.log(emailRe.test("bad-email")); // false
// 中国大陆手机号校验
const phoneRe = /^1[3-9]\d{9}$/;
console.log(phoneRe.test("13800138000")); // true
console.log(phoneRe.test("12345")); // false
// URL 校验
const urlRe = /^https?:\/\/[\w.-]+(\.[a-z]{2,6})(\/\S*)?$/i;
console.log(urlRe.test("https://example.com/page")); // true
console.log(urlRe.test("ftp://example.com")); // false
// 提取 URL 中的域名
const domainRe = /https?:\/\/([\w.-]+)/;
const match = "https://www.example.com".match(domainRe);
console.log(match[1]); // www.example.com七、综合实战:文本处理工具
javascript
// 1. 统计字符串中英文单词数量
function countWords(text) {
const words = text.trim().split(/\s+/);
return text.trim() ? words.length : 0;
}
console.log(countWords("hello world, 你好 JS")); // 4
// 2. 隐藏手机号中间四位
function maskPhone(phone) {
return phone.replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
}
console.log(maskPhone("13800138000")); // 138****8000
// 3. 金额千分位格式化
function formatMoney(num) {
return String(num).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
console.log(formatMoney(1234567)); // 1,234,567字符串方法与正则表达式是文本处理的左右手:方法负责常见的增删改查,正则负责复杂的模式匹配。建议在浏览器控制台或 Node.js 中亲手运行本节示例,观察每种方法的返回结果。