String Objects

String Objects 主要用于表示和操作字符序列

Create String

创建字符串主要有以下方式:

  1. 字面量方式
const str = 'hi';
  1. 通过String()构造函数来创建
const strObj = new String('hi');
  1. 不使用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, return SymbolDescriptiveString(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 码元序列创建的字符串。

  1. 参数codeUnits:一个介于065535(216-1)之间的数字(十进制),表示一个 UTF-16 码元序列创建的字符串。 如果大于 BMP(0xFFFF)的数字会被截断为最后的 16 位,不进行有效的检查。
  2. 返回值:一个长度为N的字符串,由N个指定的 UTF-16 码元组成。

使用

  1. 从 Unicode 值生成字符
const charA = String.fromCharCode(65);
const charB = String.fromCharCode(6554); //
  1. 处理字符编码 当你从某个数据流或二进制文件中读取字符编码值,并需要将其转换为可读字符时:
const byteArray = [72, 101, 108, 108, 111];
const readString = byteArray.reduce(
(prev, cur) => prev + String.fromCharCode(cur),
''
); // Hello
  1. 构建字符序列 可以用于生成一系列字符,例如字母表或特殊符号序列:
let strLetter = '';
for (let i = 65; i < 90; i++) {
strLetter += String.fromCharCode(i);
}
// 'ABCDEFGHIJKLMNOPQRSTUVWXY'
  1. 特殊符号或控制字符的生成
const newLine = String.fromCharCode(10); // 换行符
const tab = String.fromCharCode(9); // 制表符
const combined = 'Line1' + newLine + 'Line2' + tab + 'Tabbed';
console.log(combined);
// 输出:
// Line1
// Line2 Tabbed
  1. 处理国际化字符
const smiley = String.fromCharCode(0x263a); //输出:
const smiley = String.fromCharCode(0x101c); //输出:

fromCodePoint

String.fromCodePoint(...codePoints ) 静态方法将根据指定的码位序列返回一个字符串。

  1. 参数codePoints:一个介于 0 和 0x10FFFF(包括两者)之间的整数,表示一个 Unicode 码位。
  2. 返回值:通过使用指定的码位序列创建的字符串。
  3. 异常:如果不在范围之内,怎会抛出 RangeError 的异常。

与 fromCharCode 的比较

  • 支持的码位范围
    • String.fromCharCode 仅支持 BMP 内的码位(0x0000 到 0xFFFF)。
    • String.fromCodePoint 支持所有 Unicode 码位(0x0000 到 0x10FFFF)。
  • 处理超出 BMP 的字符
    • String.fromCharCode无法直接处理超出 BMP 的字符,需要手动处理代理对。
      // 比如 码位 U+1F303 代理对 0xd83c, 0xdf03
      String.fromCharCode(0xd83c, 0xdf03); // 码位 U+1F303(夜晚与星星)
    • String.fromCodePoint 可以直接生成超出 BMP 的字符,无需额外处理。
      // 比如 码位 U+1F303
      String.fromCodePoint(0x1f303); // 码位 U+1F303(夜晚与星星)
  • 易用性和安全性
    • String.fromCodePoint 更易用,特别是在处理包括表情符号和其他超出 BMP 的字符时,更加可靠和方便。

raw

String.raw() 静态方法是模板字符串的标签函数。它用于获取模板字符串的原始字符串形式——即,替换表达式(例如 ${foo})会被替换处理,但转义序列(例如 \n)不会被处理。

主要有以下几种用途:

  • 原样输出:String.raw 会按字面值输出模板字符串中的内容,不对转义字符进行解释或转换。
  • 多行字符串:可以用于多行字符串处理。
  • 处理特殊字符:尤其适合处理包含反斜杠或其他转义字符的字符串

使用示例

  1. 简单模板字符串
// \n 不会被解释为换行符,而是按字面值输出。
let rawString = String.raw`Hello\nWorld`; // Hello\nWorld
  1. 多行字符串
// \n 不会被解释为换行符,而是按字面值输出。
let rawString = String.raw`First line\nSecond line\nThird line`; // Hello\nWorld
  1. 生成正则表达式-在构建包含大量反斜杠的正则表达式时非常有用。
// \n 不会被解释为换行符,而是按字面值输出。
let rawString = String.raw`^\d{3}\-\d{2}\-\d{4}$`; // ^\d{3}\-\d{2}\-\d{4}$
  1. 生成文件路径:处理文件路径时,可以避免反斜杠被解释为转义字符。
// \n 不会被解释为换行符,而是按字面值输出。

Properties of prototype

at

at(index)方法接受一个整数值,并返回一个新的 String,该字符串由位于指定偏移量处的单个 UTF-16 码元组成。 该方法允许正整数和负整数。负整数从字符串中的最后一个字符开始倒数。如果没有找到,则返回undefined

const str = 'baoqi';
str.at(0); // b
str.at(1); // a
str.at(-1); // i
str.at(-2); // q
str.at(6); // undefined

规范实现步骤

charAt

charAt(pos)方法一个由给定的索引处的单个UTF-16 码元构成的新字符串

Note

charAt方法总是将字符串作为UTF-16码元序列进行索引。

因为 Unicode 码位的范围从 01114111 (0x10FFFF)。charAt() 方法总是返回一个其值小于 65536(0xFFFF)的字符。

如果获取值大于 65535 的完整字符,需要检索不仅是 charAt(i),还要检索 charAt(i + 1)(就像操作一个由两个字符组成的字符串一样), 或者使用codePointAtfromCodePoint 代替。

如果pos是一个整数,那么x.charAt(pos)的结果等同于x.substring(pos,pos+1)的结果。

示例1: 在0xFFFF范围之内的字符,UTF-16码元序列只有一个,因此这种结果是没有问题的

0xFFFF 之内的字符有很多种,详细请到这里

// 1. 基本拉丁文 码点范围 0000至007F
const str = 'my name is nate';
str.charAt(); // m
str.charAt(0); // m
str.charAt(1); // y
str.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 码元,其值介于 065535 之间,也就是 BMP 平面内。

对于超出 BMP 平面的字符,则需要按照索引分别返回高低代理项的 UTF-16 的码元值。

  1. 参数pos:要返回字符的索引,从零开始(undefined被转换为 0)
  2. 返回值:一个整数,介于 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 代码点。

  1. 参数index:从零开始(undefined被转换为 0)
  2. 返回值:一个非负整数,该整数是从给定索引开始的字符的**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 码点)的方法,下表根据不同的特性来进行比较。

atcharAtcharCodeAtcodePointAt
参数index 整数pos >= 0pos >= 0index 整数
返回值返回新的字符或者undefined返回一个字符串(一个UTF-16码元)或者空字符串返回一个介于0-65535之间的UTF-16码元值或者NaN返回Unicode码点或者undefined
BMP平面之内对应字符对应字符对应索引码元值对应Unicode码点值
BMP平面之外代理对项(转义字符返回)代理对项(转义字符返回)代理对项(各自对应的码元值)完整的Unicode码点值(索引0),索引1为低代理项Unicode码点值
使用场景提取单个字符的情况(头尾)提取单个字符的情况BMP字符的UTF-16编码整个 Unicode 字符集

concat

concat(...args) 方法将字符串参数连接到调用的字符串,并返回一个新的字符串。

  1. 参数args: 一个或者多个字符串, 也有可能是其他类型的值
  2. 返回值: 组合的新的字符串

Note

concat在组合新字符串时会将参数强制转换成字符串。内部会执行ToString或者ToPrimary

'a'.concat('b'); // ab
'a'.concat('b', 1); // ab1

endsWith

endsWith(searchString[, endPosition])方法用于判断一个字符串是否以指定字符串结尾,如果是则返回 true,否则返回 false。

  • 参数
    • searchString: 要搜索的字符串(正则会抛出异常,非正式正则被转换为字符串),参数如果是undefined,则会搜索undefined值。
    • endPosition可选: 预期找到 searchString的末尾位置,默认是字符串长度
  • 返回值: trueorfalse
'my name is nate'.endsWith('nate'); // true
'my name is nate'.endsWith('nat', 15); // true

includes

includes(searchString[, position]) 方法执行区分大小写的搜索,以确定是否可以在一个字符串中找到另一个字符串,并根据情况返回 truefalse

  • 参数
  • searchString:要搜索的字符串(正则会抛出异常,非正式正则被转换为字符串),参数如果是undefined,则会搜索undefined值。
  • position:开始的位置,默认是0。
  • 返回值:trueorfalse
'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'); // 1
str.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);
}

localeCompare

match

matchAll

normalize

padEnd

padStart

repeat

replace

replaceAll

slice

split

startsWith

substring

toLocaleLowerCase

toLocaleUpperCase

toLowerCase

toUpperCase

toString

toWellFormed

trim

trimEnd

trimStart

valueOf

[@@iterator]

字符串转换

模板字符串