String Objects
String Objects 主要用于表示和操作字符序列。
Create String
创建字符串主要有以下方式:
- 字面量方式
const str = 'hi';
- 通过
String()
构造函数来创建
const strObj = new String('hi');
- 不使用
new
调用构造函数创建
const str1 = String('hi');
这几种方式可以归纳为字符串原始值和字符串对象两种方式。
字符串字面量和不使用new
调用构造函数创建的字符串是字符串原始值,通过new
调用构造函数创建的是字符串
对象。
Note
在调用原始字符串的方法或者进行属性查找的上下文中,JavaScript会自动包装原始字符串并在包装原始字符串对象上
调用方式或者属性查找 js typeof str; // string typeof strObj // object typeof str1; // string
Pitfall
注意,在使用eval
时,原始值和对象会表现不同的效果。
2'; const strObj = new String('2 + 2'); eval(str); // 4 eval(strObj); //String{'2 + 2'}
字符串对象转换成字符串原始值转换
strObj.valueOf(); // '2 + 2'
字符串比较
字符串的比较是基于字符串中字符的Unicode 编码值来决定顺序的。 主要有以下几种方式:
- 比较运算符
- 逐字比较
- 大小写比较:Unicode 码点(大小写不同)
- 语言环境比较:
localeCompare()
Constructor
String()
构造函数创建字符串对象,当作为函数调用时,作为字符串原始值并且进行类型转换。
String(value)
调用时执行的步骤: - If value is not present, then - Let s be the empty String. - else - If NewTarget is undefined and value is a Symbol, returnSymbolDescriptiveString(value)
. - Let s be ? ToString(value). - If NewTarget is undefined, return s. - Return StringCreate(s, ?GetPrototypeFromConstructor(NewTarget, "%String.prototype%"))
.
需要注意的是否是函数调用还是构造函数,其次是value
的值类型,这里具体要看ToString
的内部实现方式。
还有String(Symbol('s'))
是唯一一种将 Symbol 转换为 String 不抛出异常的转换方式。
Static Methods
fromCharCode
String.fromCharCode(...codeUnits)
静态方法返回由指定的 UTF-16 码元序列创建的字符串。
- 参数
codeUnits
:一个介于0
到65535
(216-1)之间的数字(十进制),表示一个 UTF-16 码元序列创建的字符串。 如果大于 BMP(0xFFFF)的数字会被截断为最后的 16 位,不进行有效的检查。 - 返回值:一个长度为
N
的字符串,由N
个指定的 UTF-16 码元组成。
使用
- 从 Unicode 值生成字符
const charA = String.fromCharCode(65);const charB = String.fromCharCode(6554); //
- 处理字符编码 当你从某个数据流或二进制文件中读取字符编码值,并需要将其转换为可读字符时:
const byteArray = [72, 101, 108, 108, 111];const readString = byteArray.reduce((prev, cur) => prev + String.fromCharCode(cur),''); // Hello
- 构建字符序列 可以用于生成一系列字符,例如字母表或特殊符号序列:
let strLetter = '';for (let i = 65; i < 90; i++) {strLetter += String.fromCharCode(i);}// 'ABCDEFGHIJKLMNOPQRSTUVWXY'
- 特殊符号或控制字符的生成
const newLine = String.fromCharCode(10); // 换行符const tab = String.fromCharCode(9); // 制表符const combined = 'Line1' + newLine + 'Line2' + tab + 'Tabbed';console.log(combined);// 输出:// Line1// Line2 Tabbed
- 处理国际化字符
const smiley = String.fromCharCode(0x263a); //输出:const smiley = String.fromCharCode(0x101c); //输出:
fromCodePoint
String.fromCodePoint(...codePoints )
静态方法将根据指定的码位序列返回一个字符串。
- 参数
codePoints
:一个介于 0 和 0x10FFFF(包括两者)之间的整数,表示一个 Unicode 码位。 - 返回值:通过使用指定的码位序列创建的字符串。
- 异常:如果不在范围之内,怎会抛出 RangeError 的异常。
与 fromCharCode 的比较
- 支持的码位范围
String.fromCharCode
仅支持 BMP 内的码位(0x0000 到 0xFFFF)。String.fromCodePoint
支持所有 Unicode 码位(0x0000 到 0x10FFFF)。
- 处理超出 BMP 的字符
String.fromCharCode
无法直接处理超出 BMP 的字符,需要手动处理代理对。// 比如 码位 U+1F303 代理对 0xd83c, 0xdf03String.fromCharCode(0xd83c, 0xdf03); // 码位 U+1F303(夜晚与星星)String.fromCodePoint
可以直接生成超出 BMP 的字符,无需额外处理。// 比如 码位 U+1F303String.fromCodePoint(0x1f303); // 码位 U+1F303(夜晚与星星)
- 易用性和安全性
String.fromCodePoint
更易用,特别是在处理包括表情符号和其他超出 BMP 的字符时,更加可靠和方便。
raw
String.raw() 静态方法是模板字符串的标签函数。它用于获取模板字符串的原始字符串形式——即,替换表达式(例如 ${foo}
)会被替换处理,但转义序列(例如 \n)不会被处理。
主要有以下几种用途:
- 原样输出:String.raw 会按字面值输出模板字符串中的内容,不对转义字符进行解释或转换。
- 多行字符串:可以用于多行字符串处理。
- 处理特殊字符:尤其适合处理包含反斜杠或其他转义字符的字符串
使用示例
- 简单模板字符串
// \n 不会被解释为换行符,而是按字面值输出。let rawString = String.raw`Hello\nWorld`; // Hello\nWorld
- 多行字符串
// \n 不会被解释为换行符,而是按字面值输出。let rawString = String.raw`First line\nSecond line\nThird line`; // Hello\nWorld
- 生成正则表达式-在构建包含大量反斜杠的正则表达式时非常有用。
// \n 不会被解释为换行符,而是按字面值输出。let rawString = String.raw`^\d{3}\-\d{2}\-\d{4}$`; // ^\d{3}\-\d{2}\-\d{4}$
- 生成文件路径:处理文件路径时,可以避免反斜杠被解释为转义字符。
// \n 不会被解释为换行符,而是按字面值输出。
Properties of prototype
at
at(index)
方法接受一个整数值,并返回一个新的 String,该字符串由位于指定偏移量处的单个 UTF-16 码元组成。
该方法允许正整数和负整数。负整数从字符串中的最后一个字符开始倒数。如果没有找到,则返回undefined
。
const str = 'baoqi';str.at(0); // bstr.at(1); // astr.at(-1); // istr.at(-2); // qstr.at(6); // undefined
规范实现步骤。
charAt
charAt(pos)
方法一个由给定的索引处的单个UTF-16 码元构成的新字符串。
Note
charAt
方法总是将字符串作为UTF-16码元序列进行索引。
因为 Unicode 码位的范围从 0
到 1114111
(0x10FFFF)。charAt()
方法总是返回一个其值小于 65536(0xFFFF)的字符。
如果获取值大于 65535 的完整字符,需要检索不仅是 charAt(i)
,还要检索 charAt(i + 1)
(就像操作一个由两个字符组成的字符串一样),
或者使用codePointAt、fromCodePoint 代替。
如果pos是一个整数,那么x.charAt(pos)
的结果等同于x.substring(pos,pos+1)
的结果。
示例1: 在0xFFFF范围之内的字符,UTF-16码元序列只有一个,因此这种结果是没有问题的
0xFFFF 之内的字符有很多种,详细请到这里
// 1. 基本拉丁文 码点范围 0000至007Fconst str = 'my name is nate';str.charAt(); // mstr.charAt(0); // mstr.charAt(1); // ystr.charAt(100); // ''// 2. '☎' 码点 U+260E
示例2: 在0xFFFF范围之外的字符,UTF-16码元序列有多个,因此这种结果是需要找到每一个码元
这种字符一般都是在第一到第十六辅助平面之内,详细到这里
接下来验证一下,字符𠀗
的码点 U+20017,根据UTF-16 编码之后的码元序列为0xD840 0xDC17
。
因此当执行charAt(0)
会得到0xD840
码元,以此类推,后面会得到剩余的码元,直到为空。
At方法也存在这种0xFFFF范围之外的问题,结果跟charAt方法一致。
At 和 charAt 之间比较明显的区别是通过索引来获取或者截取字符,尤其是从字符结尾开始的场景。
charCodeAt
charCodeAt(pos)
方法返回一个整数,表示给定索引处的 UTF-16 码元,其值介于 0
和 65535
之间,也就是 BMP 平面内。
对于超出 BMP 平面的字符,则需要按照索引分别返回高低代理项的 UTF-16 的码元值。
- 参数pos:要返回字符的索引,从零开始(
undefined
被转换为 0) - 返回值:一个整数,介于 0-65535。
- 超出 65535 的字符,则会按照代理项返回对应码元
- 索引pos不在字符长度范围之内,返回 NaN
示例1: 在0xFFFF范围之内的字符,UTF-16码元序列只有一个,这种是不会有代理对的
'name'.charCodeAt(0); // 110 -> n'name'.charCodeAt(1); // 97'name'.charCodeAt(2); // 109'name'.charCodeAt(3); // 101'name'.charCodeAt(4); // NaN'你'.charCodeAt(0); // 20320'你'.charCodeAt(1); // NaN
示例2: 在0xFFFF范围之外的字符,UTF-16码元序列有两个,这种是存在代理对的,索引0
会得到对应的码点。
'𠀗'.charCodeAt(0); // 55360 - 高位代理项'𠀗'.charCodeAt(1); // 56343 - 低位代理项'𠀗'.charCodeAt(2); // NaN
**如果想要一次得到 0xFFFF 范围之外的字符的 UTF,可以使用 *codePointAt*方法。
codePointAt
codePointAt(index)
方法返回一个非负整数,该整数是从给定索引开始的字符的Unicode 码点值(十进制)。
请注意,索引仍然基于 UTF-16 代码单元,而不是 Unicode 代码点。
- 参数index:从零开始(
undefined
被转换为 0) - 返回值:一个非负整数,该整数是从给定索引开始的字符的**Unicode 码点值(十进制)。
- 如果索引超出范围,即
0 - str.lenght - 1
,返回undefined
。 - 如果索引指向的元素是 UTF-16 高位代理项,则返回代理对的码点值。
- 如果索引指向的元素是 UTF-16 低位代理项,则仅仅返回低位代理项的码点值。
示例1: 在0xFFFF范围之内的字符,UTF-16码元序列只有一个,这种是不会有代理对的
'name'.codePointAt(0); // 110 -> n'name'.codePointAt(1); // 97'name'.codePointAt(2); // 109'name'.codePointAt(3); // 101'name'.codePointAt(4); // undefined
示例2: 在0xFFFF范围之外的字符,UTF-16码元序列有两个,这种是存在代理对的,索引0
会得到整个码点。
'😁'.codePointAt(0).toString(16); // 0x1f601'😁'.codePointAt(1).toString(16); // 0xde01 只会得到对应索引代理项的码点
示例3: 如果给出字符的转义序列,返回其Unicode码点值
// 1. 如果使用for循环,则同一个Unicode码点会被访问两次(因为一个码点可能是代理对),得到的结果是不对的// 2. 代替的方案,使用for-of循环或者...str,两者都调用了`@@iterator`方法, 使用codePointAt(0)获取每个元素的代码点。for (const char of str) {console.log(char.codePointAt(0));}[...str].map((char) => char.codePointAt(0));
上述四种对比
基于上述四种都是根据索引获取字符或者字符(UTF-16 码元或者 Unicode 码点)的方法,下表根据不同的特性来进行比较。
at | charAt | charCodeAt | codePointAt | |
---|---|---|---|---|
参数 | index 整数 | pos >= 0 | pos >= 0 | index 整数 |
返回值 | 返回新的字符或者undefined | 返回一个字符串(一个UTF-16码元)或者空字符串 | 返回一个介于0-65535之间的UTF-16码元值或者NaN | 返回Unicode码点或者undefined |
BMP平面之内 | 对应字符 | 对应字符 | 对应索引码元值 | 对应Unicode码点值 |
BMP平面之外 | 代理对项(转义字符返回) | 代理对项(转义字符返回) | 代理对项(各自对应的码元值) | 完整的Unicode码点值(索引0),索引1为低代理项Unicode码点值 |
使用场景 | 提取单个字符的情况(头尾) | 提取单个字符的情况 | BMP字符的UTF-16编码 | 整个 Unicode 字符集 |
concat
concat(...args)
方法将字符串参数连接到调用的字符串,并返回一个新的字符串。
- 参数args: 一个或者多个字符串, 也有可能是其他类型的值
- 返回值: 组合的新的字符串
'a'.concat('b'); // ab'a'.concat('b', 1); // ab1
endsWith
endsWith(searchString[, endPosition])
方法用于判断一个字符串是否以指定字符串结尾,如果是则返回 true,否则返回 false。
- 参数
- searchString: 要搜索的字符串(正则会抛出异常,非正式正则被转换为字符串),参数如果是
undefined
,则会搜索undefined
值。 - endPosition可选: 预期找到 searchString的末尾位置,默认是字符串长度
- searchString: 要搜索的字符串(正则会抛出异常,非正式正则被转换为字符串),参数如果是
- 返回值:
true
orfalse
'my name is nate'.endsWith('nate'); // true'my name is nate'.endsWith('nat', 15); // true
includes
includes(searchString[, position])
方法执行区分大小写的搜索,以确定是否可以在一个字符串中找到另一个字符串,并根据情况返回 true
或 false
。
- 参数
- searchString:要搜索的字符串(正则会抛出异常,非正式正则被转换为字符串),参数如果是
undefined
,则会搜索undefined
值。 - position:开始的位置,默认是0。
- 返回值:
true
orfalse
'my name is nate'.includes('nate'); // true'my name is nate'.includes('nat', 2); // true
indexOf
indexOf(searchString[, position])
方法在字符串中搜索指定子字符串,并返回其第一次出现的位置索引。它可以接受一个可选的参数指定搜索的起始位置,如果找到了指定的子字符串,
则返回的位置索引大于或等于指定的数字。
- 参数
- searchString: 需要搜索的字符串,所有传入值都被转换为字符串,如果未传,则是需要去搜索
undefined
。 - position可选:该方法返回指定子字符串在大于或等于 position 位置的第一次出现的索引,默认为 0。
- 返回值:查找的字符串第一次出现的索引,否则,返回
-1
Note
indexOf
是区分字符串的大小写。
const str = 'my name is Nate';str.indexOf('y'); // 1str.indexOf('nat'); // -1
lastIndexOf
lastIndexOf()
方法搜索该字符串并返回指定子字符串最后一次出现的索引。它可以接受一个可选的起始位置参数,
并返回指定子字符串在小于或等于指定数字的索引中的最后一次出现的位置。
- 参数
- searchString: 需要搜索的字符串,所有传入值都被转换为字符串,如果未传,则是需要去搜索
undefined
。 - position可选:该方法返回指定子字符串在小于或等于 position 的位置中的最后一次出现的索引,默认为 +Infinity。
- 返回值:查找的字符串第一次出现的索引,否则,返回
-1
**跟indexOf()**同样的搜索算法,只不过开始搜索的位置不一样。
isWellFormed
isWellFormed()
方法返回一个表示该字符串是否包含单独代理项的布尔值。
返回值:如果字符串不包含单独代理项,返回 true,否则返回 false。
Note
应用场景: 避免encodeURL
报错
如果传递的字符串格式不正确, encodeURI
会抛出错误。可以通过使用 isWellFormed()
在将字符串传递给 encodeURI()
之前测试字符串来避免这种情况。
const url = "https://example.com/search?q=";if(url.isWellFormed()) {encodeURI(url);}