总结
JavaScript 的字符以 UTF-16 格式存储,每个字符占两个字节;对于补充平面字符(码点大于 U+FFFF),每个字符占四个字节,length 为 2。字符串一旦创建不可变,无法修改单个字符。
创建字符串
// 字面量
var str1 = 'hello';
var str2 = "world";
// String 转换
String(123); // "123"
String(true); // "true"
String(null); // "null"
多行字符串用反斜杠续行(反斜杠后不能有空格)或字符串拼接:
var longString = 'Long \
long \
string';
// "Long long string"
转义字符
反斜杠(\)表示特殊字符:\n 换行、\t 制表符、\\ 反斜杠、\' 单引号、\" 双引号、\0 null。
特殊表示法:
\xHH:两个十六进制数,如\xA9表示 ©\uXXXX:四个十六进制数,如©表示 ©
字符串不可变性
var s = 'hello';
s[1] = 'a'; // 无效
delete s[0]; // 无效
s // "hello"
所有”修改”操作都返回新字符串,不改变原字符串。
常用方法
查找
var str = 'Hello World, Hello JS';
str.indexOf('Hello'); // 0
str.lastIndexOf('Hello'); // 13
str.indexOf('Hello', 1); // 13(从位置 1 开始搜索)
str.includes('World'); // true
str.startsWith('Hello'); // true
str.endsWith('JS'); // true
str.search(/world/i); // 6(支持正则)
截取
var str = 'Hello World';
// slice(start, end):支持负数索引
str.slice(0, 5); // "Hello"
str.slice(6); // "World"
str.slice(-5); // "World"
// substring(start, end):不支持负数
str.substring(0, 5); // "Hello"
// 推荐使用 slice,语义更清晰
替换
var str = 'hello world hello';
str.replace('hello', 'hi'); // "hi world hello"(只替换第一个)
str.replace(/hello/g, 'hi'); // "hi world hi"(全局替换)
// 替换函数
'abc123'.replace(/\d+/, function(match) {
return Number(match) * 2;
});
// "abc246"
分割与拼接
'a,b,c'.split(','); // ["a", "b", "c"]
'hello'.split(''); // ["h", "e", "l", "l", "o"]
'a,b,c,d'.split(',', 2); // ["a", "b"]
'Hello'.concat(' ', 'World'); // "Hello World"
大小写与去空格
'Hello'.toUpperCase(); // "HELLO"
'Hello'.toLowerCase(); // "hello"
' hello '.trim(); // "hello"
' hello '.trimStart(); // "hello "
' hello '.trimEnd(); // " hello"
其他
'ha'.repeat(3); // "hahaha"
'5'.padStart(3, '0'); // "005"
'hi'.padEnd(5, '.'); // "hi..."
'abc'.charAt(1); // "b"
'abc'.charCodeAt(0); // 97
模板字符串
ES6 模板字符串使用反引号,支持多行和表达式插值:
var name = 'World';
var greeting = `Hello ${name}!`; // "Hello World!"
// 多行
var html = `
<div>
<p>${greeting}</p>
</div>
`;
// 表达式和函数调用
var a = 10, b = 20;
`${a} + ${b} = ${a + b}`; // "10 + 20 = 30"
标签模板(Tagged Template):
function highlight(strings, ...values) {
return strings.reduce(function(result, str, i) {
return result + str + (values[i] ? '<em>' + values[i] + '</em>' : '');
}, '');
}
var user = 'Tom';
highlight`User ${user} logged in`;
// "User <em>Tom</em> logged in"
正则配合
// match:返回匹配数组
'test 123 hello 456'.match(/\d+/g); // ["123", "456"]
// 捕获组
var result = '2018-01-15'.match(/(\d{4})-(\d{2})-(\d{2})/);
result[1]; // "2018"
result[2]; // "01"
result[3]; // "15"
// split 配合正则
'a1b2c3'.split(/\d/); // ["a", "b", "c", ""]
// replace 中使用捕获组
'2018-01-15'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$2/$3/$1');
// "01/15/2018"
Unicode 处理
对于码点大于 U+FFFF 的字符(如 emoji),JavaScript 将其视为两个字符:
'𝌆'.length; // 2
'😀'.length; // 2
// 正确获取长度
Array.from('😀hello').length; // 6
// ES6 码点方法
'𝌆'.codePointAt(0); // 119558
String.fromCodePoint(119558); // "𝌆"
// for...of 能正确遍历四字节字符
for (var ch of '😀hi') {
console.log(ch); // 😀, h, i
}
Base64 编码
用于将二进制数据或不可打印字符转为可打印的 ASCII 字符:
btoa('Hello World!'); // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh'); // "Hello World!"
// 非 ASCII 字符需要先编码
function b64Encode(str) {
return btoa(encodeURIComponent(str));
}
function b64Decode(str) {
return decodeURIComponent(atob(str));
}
b64Encode('你好'); // "JUU0JUJEJUEwJUU1JUE1JUJE"
b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE'); // "你好"
实际应用
URL 参数解析
function parseQuery(queryString) {
return queryString.replace(/^\?/, '').split('&')
.reduce(function(params, pair) {
var parts = pair.split('=');
params[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1] || '');
return params;
}, {});
}
parseQuery('?name=Tom&age=20'); // { name: "Tom", age: "20" }
驼峰与短横线转换
function toCamelCase(str) {
return str.replace(/-([a-z])/g, function(match, letter) {
return letter.toUpperCase();
});
}
toCamelCase('background-color'); // "backgroundColor"
function toKebabCase(str) {
return str.replace(/[A-Z]/g, function(letter) {
return '-' + letter.toLowerCase();
});
}
toKebabCase('backgroundColor'); // "background-color"