4. String
1. 通用知识
在计算机编程中,字符串通常是一个字符序列,可以是字面量,也可以是变量。后者可能会允许修改元素和改变长度,也可能是固定的(在创建之后)。
字符串是一种数据类型,通常被实现为 bytes/characters/code units 的数组,存储着元素序列,元素通常是字符且会使用某种字符编码。用“数组”实现是为了快速访问单个单元的字符和子字符串,也有个别编程语言是用链表来实现的(比如 Haskell)。
字符串通常由字符组成。可用于存储人类可读的数据,如句子、按字母顺序排列的数据列表。
根据使用的编程语言和其提供的具体数据类型,声明为字符串的变量在内存中的存储,可能是静态分配预留了最大长度,也可能是动态分配以确保元素的数量是可变的。
字符串是一种非常重要且有用的数据类型,几乎所有的编程语言都实现了它。在不同的编程语言里,字符串可能是原始类型,也可能是复合类型。当字符串以字面量的形式出现在源代码中的时候,被称为是字符串字面量或匿名字符串。
定长+变长、字符集+字符编码、原始+复合类型、字面量
2. String 类型
在 JavaScript 里,String 类型用来表示文本数据,它是一组 16 位无符号整数值的“元素”。
字符串里的每个元素都占了一个位置,我们可以通过下标来访问每个元素,下标从 0 开始。字符串的长度就是它里面元素的个数。如下:
let foo = 'JavaScript';
foo[0]; // "J"
foo[1]; // "a"
foo.length; // 10
3. String 对象
在 JavaScript 中,String 对象是 String 原始值的对象包装器,用来表示和操作字符序列。
3.1 构造器
new String()
返回对象,搭配 new 关键字作为构造函数用String()
返回字符串,直接当函数用可做类型转换(此方式更有用)
3.2 静态方法
String.fromCharCode()
传 Unicode 值序列String.fromCodePoint()
传代码点序列String.raw()
传原始模板字符串
3.3 实例属性
String.prototype.length
只读
3.4 实例方法
String.prototype.xxx
3.4.1 通用方法
valueOf()
toString()
3.4.2 常用操作
下标→单个字符
at()
charAt()
charCodeAt()
codePointAt()
字符串→下标
indexOf()
lastIndexOf()
是否以指定字符串开始、结束、包含
startsWith()
endsWith()
includes()
提取子串,传起始下标
slice()
substring()
已不推荐使用substr()
正则表达式
search()
match()
,matchAll()
replace()
,replaceAll()
split()
变数组concat()
拼接大小写
toLowerCase()
toUpperCase()
toLocaleLowerCase()
toLocaleUpperCase()
去首尾空格
trim()
trimStart()
trimEnd()
前后对齐
padEnd()
padStart()
3.4.3 其它
normalize()
返回 Unicode 正规形式repeat()
传重复次数localeCompare()
比较@@iterator()
迭代器
4. 常见操作
4.1 创建字符串
创建字符串原始值,可以使用字符串字面量和函数。如下:
let str1 = 'hello world'; // 单引号,字面量
let str2 = "hello world"; // 双引号,字面量
let str3 = `hello world`; // 字符串模板,字面量
let str4 = String('hello world'); // 函数
创建字符串对象,可以使用字符串对象的构造器。如下:
let str5 = new String('hello world'); // new
console.log(str5.valueOf()); // hello world
4.2 原始值和对象
在大多数情况下,字符串原始值和字符串对象是可以互换使用的。JavaScript 会自动把字符串原始值转成对象,以便在字符串原始值上使用字符串对象的方法。
但是,字符串原始值和字符串对象,还是有些区别的。
区别一:typeof
的结果不同
typeof 'hello' === 'string'; // true
typeof (new String('hello')) === 'object'; // true
区别二:eval()
的结果不同
let str1 = '1+2';
let str2 = new String('1+2');
console.log(eval(str1)); // 会输出数字 3
console.log(eval(str2)); // 会输出字符串 '1+2'
当然,可以通过手动调用 valueOf()
来“修复”。如下:
console.log(eval(str2.valueOf())); // 此时会输出数字 3
4.3 访问单个字符
方法一:使用 charAt()
console.log('hello'.charAt(1)); // "e"
方法二:使用 []
。这是在 ES5 里引入的,它把字符串视为类数组(array-like)对象,然后单个字符对应一个数字索引。
console.log('hello'[1]); // "e"
当使用[]
进行字符访问的时候,尝试删除或者重新赋值是不会生效的,因为所涉及的属性是不可写、不可配置的。
4.4 字符串比较
可以用运算符>
和<
以及实例方法 localeCompare()
。
这里需要注意的是,用==
运算符比较的两个字符串是大小写敏感的。如果不想区分大小写,可以先都转成大写再用===
。如下:
function isEqual(str1, str2){
return str1.toUpperCase() === str2.toUpperCase();
}
isEqual('javascript', 'JavaScript'); // 会返回 true
这里用了toUpperCase()
而不是toLowerCase()
,是考虑到某些 UTF-8 字符转换的问题。
4.5 类型转换
和 .toString()
相比,String()
更可靠,因为它还适用于 undefined, null 和 symbol。
5. 注意事项
5.1 长字符串字面量
方法一:使用操作符 +
。如下:
let longStr1 = 'This is a very very long string ' +
'which needs to wrap across multiple lines ' +
'because otherwise your code is unreadable.'
方法二:在行尾加 \
,以表示该字符串还会在下一行继续。如下:
let longStr2 = 'This is a very very long string \
which needs to wrap across multiple lines \
because otherwise your code is unreadable.'
注意:在\
之后不能有空格、注释和其它任何字符(换行符除外),否则它就不工作了。
5.2 输出多行字符串
可使用模板字符串,它支持直接换行。
console.log(`This is a very very long string
which needs to wrap across multiple lines
because otherwise your code is unreadable.`);
源代码里的任何字符(包括换行符)都是模板字面量的一部分,所以上面的字符串里是含有字符的。
5.3 慎用字符串模拟数组
在实际使用中,我们可能会用字符串来模拟数组/列表,但这么做是有潜在风险的。
要么分隔符可能就是列表里的元素本身,要么就得挑个特殊的字符来当分隔符。前者可能会导致列表不工作,后者不仅要约定特殊字符而且还会增加后续的维护成本。
所以,尽量用字符串表示文本数据。
5.4 字符集和编码方式
这个话题较为独立,详见《Unicode 字符编码模型》。
6. 主要参考
Last updated