字符串是开发中使用频率最高的数据类型之一。JavaScript 中的字符串既可以是基本类型,也可以被当作对象来调用方法(背后会临时包装成 String 对象)。ES6 引入的模板字符串和标签模板更是让字符串处理能力提升了一个台阶。本节聚焦日常开发中最实用、最高频的字符串能力,不讲冷门边角。
10.2.1 字符串常用方法速查
以下方法按使用场景分类,全部基于字符串不可变的特性——任何一个方法都不会修改原字符串,而是返回新字符串。
查找与判断
const str = 'Hello JavaScript';
// 获取指定位置字符
str.charAt(6); // 'J'
str[6]; // 'J',更简洁
// 查找子串位置
str.indexOf('Java'); // 6,不存在返回 -1
str.lastIndexOf('a'); // 9,从后往前找
// 是否包含、开头、结尾(ES6+)
str.includes('Java'); // true
str.startsWith('Hello'); // true
str.endsWith('Script'); // true
实用技巧:用 includes() 替代 indexOf !== -1 更可读;判断文件扩展名用 endsWith()。
截取与拆分
str.slice(6, 10); // 'Java',支持负数索引,相当于 length + (-4)
str.substring(6, 10); // 'Java',不支持负数,会自动交换参数
str.split(' '); // ['Hello', 'JavaScript'],转成数组
选型建议:日常推荐 slice,行为统一好记;substring 和 substr 有历史包袱,非必要不用。
替换
const text = 'apple apple';
text.replace('apple', 'orange'); // 'orange apple',只替换第一个
text.replaceAll('apple', 'orange'); // 'orange orange',全部替换
text.replace(/apple/g, 'orange'); // 正则全局替换
实战要点:replace 的第二个参数可以是函数,实现动态替换,常用于模板引擎或字符串格式化:
const tpl = 'Hello, ${name}! Today is ${day}.';
tpl.replace(/\$\{(\w+)\}/g, (match, key) => ({ name: 'Tom', day: 'Monday' }[key]));
// 'Hello, Tom! Today is Monday.'
大小写转换
'JavaScript'.toUpperCase(); // 'JAVASCRIPT'
'JavaScript'.toLowerCase(); // 'javascript'
去除空格
' hello '.trim(); // 'hello',去除首尾空格
' hello '.trimStart(); // 'hello ',去除头部空格
' hello '.trimEnd(); // ' hello',去除尾部空格
填充与重复
'9'.padStart(2, '0'); // '09',常用于日期补零
'9'.padEnd(2, '0'); // '90'
'ha'.repeat(3); // 'hahaha'
拼接
// 传统拼接
'Hello' + ' ' + 'World'; // 'Hello World'
// concat 方法
'Hello'.concat(' ', 'World'); // 'Hello World'
// 大量字符串拼接时,用数组 join 或模板字符串性能更优
const parts = ['Hello', 'World'];
parts.join(' '); // 'Hello World'
10.2.2 模板字符串:更优雅的字符串表达
ES6 带来的模板字符串(反引号 ` ``)解决了传统字符串的两大痛点:多行文本和变量嵌入。
多行字符串
不需要再拼接 \n:
const html = `
<div>
<h1>Title</h1>
</div>
`;
// 输出保留换行和缩进
注意:模板字符串内的空格和换行会被完整保留,如果不需要缩进可以配合 trim() 或格式处理。
表达式插值
${} 中可以放入任何 JavaScript 表达式,自动转换为字符串:
const name = 'Tom';
const age = 25;
const msg = `Hello, ${name}. You are ${age} years old. Next year you will be ${age + 1}.`;
支持运算、三元表达式、函数调用,甚至嵌套模板。
对比总结
| 方式 | 多行 | 变量 | 可读性 |
|------|------|------|--------|
| + 拼接 | 需 \n | + var + | 差 |
| array.join | 一般 | 手动插入 | 一般 |
| 模板字符串 | 原生支持 | ${} 直接嵌入 | 优秀 |
10.2.3 标签模板:字符串的“预处理”能力
标签模板允许在模板字符串前面附加一个函数,该函数可以对模板的静态部分和动态表达式分别处理,常用于 DSL(领域特定语言)、国际化、XSS 防护等场景。
基本语法
function tag(strings, ...values) {
console.log(strings); // 静态部分的数组
console.log(values); // 插值表达式的值数组
}
tag`Hello ${name}, you have ${count} messages.`;
// strings: ['Hello ', ', you have ', ' messages.']
// values: ['Alice', 5]
标签函数的第一个参数是一个数组,包含所有非动态的文本片段;剩余参数是每个 ${} 的求值结果。注意:静态片段数组的长度永远比动态值多 1(即使首或尾是表达式,它也会有空字符串填充)。
拼装结果
标签函数必须返回一个字符串(或任何你需要的数据结构),可将静态片段和动态值重新组合:
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] ? `<em>${values[i]}</em>` : '';
return result + str + value;
}, '');
}
const user = 'Tom';
const action = 'commented';
highlight`${user} has ${action} on your post.`;
// '<em>Tom</em> has <em>commented</em> on your post.'
原始字符串 raw
标签函数的第一个参数数组还有一个隐藏属性 raw,可以拿到未被转义的原始字符串(反斜杠保持原样),对于编写代码高亮、正则生成等工具非常有用。
function rawTag(strings) {
console.log(strings[0]); // '换行符:\n'
console.log(strings.raw[0]); // '换行符:\\n'
}
rawTag`换行符:\n`;
实战应用举例
- 防止 XSS 攻击:在标签函数中对动态值进行 HTML 转义,确保用户输入不会引入脚本。
- 样式组件:styled-components 等 CSS-in-JS 方案就是通过标签模板实现。
- 国际化 i18n:根据语言环境替换动态值格式。
10.2.4 结语
字符串处理是前端开发的“内功”。熟练运用原生方法可以少装依赖,模板字符串让拼接变得更直观,标签模板则打开了元编程的一扇窗。日常开发中,优先使用模板字符串提升可读性,在需要安全过滤或构建特定领域语言时,再考虑引入标签模板。