> For the complete documentation index, see [llms.txt](https://anjia1.gitbook.io/web/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://anjia1.gitbook.io/web/js/data-type/primitive/string.md).

# 4. String

## 1. 通用知识

在计算机编程中，字符串通常是一个字符序列，可以是字面量，也可以是变量。后者可能会允许修改元素和改变长度，也可能是固定的（在创建之后）。

字符串是一种数据类型，通常被实现为 bytes/characters/code units 的数组，存储着元素序列，元素通常是字符且会使用某种字符编码。用“数组”实现是为了快速访问单个单元的字符和子字符串，也有个别编程语言是用链表来实现的（比如 Haskell）。

![](https://user-images.githubusercontent.com/7055639/131313617-bfb29050-2b54-4e92-93f1-77a9453b59f2.png)

字符串通常由字符组成。可用于存储人类可读的数据，如句子、按字母顺序排列的数据列表。

根据使用的编程语言和其提供的具体数据类型，声明为字符串的变量在内存中的存储，可能是静态分配预留了最大长度，也可能是动态分配以确保元素的数量是可变的。

字符串是一种非常重要且有用的数据类型，几乎所有的编程语言都实现了它。在不同的编程语言里，字符串可能是原始类型，也可能是复合类型。当字符串以字面量的形式出现在源代码中的时候，被称为是字符串字面量或匿名字符串。

> 定长+变长、字符集+字符编码、原始+复合类型、字面量

## 2. String 类型

在 JavaScript 里，String 类型用来表示文本数据，它是一组 16 位无符号整数值的“元素”。

字符串里的每个元素都占了一个位置，我们可以通过下标来访问每个元素，下标从 0 开始。字符串的长度就是它里面元素的个数。如下：

```javascript
let foo = 'JavaScript';
foo[0];  // "J"
foo[1];  // "a"
foo.length;  // 10
```

## 3. String 对象

在 JavaScript 中，String 对象是 String 原始值的对象包装器，用来表示和操作字符序列。

### 3.1 构造器

1. `new String()` 返回对象，搭配 new 关键字作为构造函数用
2. `String()` 返回字符串，直接当函数用可做类型转换（此方式更有用）

### 3.2 静态方法

1. `String.fromCharCode()` 传 Unicode 值序列
2. `String.fromCodePoint()` 传代码点序列
3. `String.raw()` 传原始模板字符串

### 3.3 实例属性

1. `String.prototype.length` 只读

### 3.4 实例方法

`String.prototype.xxx`

#### 3.4.1 通用方法

1. `valueOf()`
2. `toString()`

#### 3.4.2 常用操作

1. 下标→单个字符
   * `at()`
   * `charAt()`
   * `charCodeAt()`
   * `codePointAt()`
2. 字符串→下标
   * `indexOf()`
   * `lastIndexOf()`
3. 是否以指定字符串开始、结束、包含
   * `startsWith()`
   * `endsWith()`
   * `includes()`
4. 提取子串，传起始下标
   * `slice()`
   * `substring()`
   * ~~`substr()`~~ 已不推荐使用
5. 正则表达式
   * `search()`
   * `match()`, `matchAll()`
   * `replace()`, `replaceAll()`
6. `split()` 变数组
7. `concat()` 拼接
8. 大小写
   * `toLowerCase()`
   * `toUpperCase()`
   * `toLocaleLowerCase()`
   * `toLocaleUpperCase()`
9. 去首尾空格
   * `trim()`
   * `trimStart()`
   * `trimEnd()`
10. 前后对齐
    * `padEnd()`
    * `padStart()`

#### 3.4.3 其它

1. `normalize()` 返回 Unicode 正规形式
2. `repeat()` 传重复次数
3. `localeCompare()` 比较
4. `@@iterator()` 迭代器

## 4. 常见操作

### 4.1 创建字符串

创建字符串原始值，可以使用字符串字面量和函数。如下：

```javascript
let str1 = 'hello world';  // 单引号，字面量
let str2 = "hello world";  // 双引号，字面量
let str3 = `hello world`;  // 字符串模板，字面量

let str4 = String('hello world');  // 函数
```

创建字符串对象，可以使用字符串对象的构造器。如下：

```javascript
let str5 = new String('hello world');  // new
console.log(str5.valueOf());  // hello world
```

### **4.2 原始值和对象**

在大多数情况下，字符串原始值和字符串对象是可以互换使用的。JavaScript 会自动把字符串原始值转成对象，以便在字符串原始值上使用字符串对象的方法。

但是，字符串原始值和字符串对象，还是有些区别的。

区别一：`typeof` 的结果不同

```javascript
typeof 'hello' === 'string';                // true
typeof (new String('hello')) === 'object';  // true
```

区别二：`eval()` 的结果不同

```javascript
let str1 = '1+2';
let str2 = new String('1+2');
console.log(eval(str1));  // 会输出数字 3
console.log(eval(str2));  // 会输出字符串 '1+2'  
```

当然，可以通过手动调用 `valueOf()` 来“修复”。如下：

```javascript
console.log(eval(str2.valueOf()));  // 此时会输出数字 3
```

### 4.3 访问单个字符

方法一：使用 `charAt()`

```javascript
console.log('hello'.charAt(1));  // "e"
```

方法二：使用 `[]`。这是在 ES5 里引入的，它把字符串视为类数组（array-like）对象，然后单个字符对应一个数字索引。

```javascript
console.log('hello'[1]);  // "e"
```

当使用`[]`进行字符访问的时候，尝试删除或者重新赋值是不会生效的，因为所涉及的属性是不可写、不可配置的。

### 4.4 字符串比较

可以用运算符`>`和`<` 以及实例方法 `localeCompare()`。

这里需要注意的是，用`==`运算符比较的两个字符串是大小写敏感的。如果不想区分大小写，可以先都转成大写再用`===`。如下：

```javascript
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 长字符串字面量

方法一：使用操作符 `+`。如下：

```javascript
let longStr1 = 'This is a very very long string ' +
               'which needs to wrap across multiple lines ' +
               'because otherwise your code is unreadable.'
```

方法二：在行尾加 `\`，以表示该字符串还会在下一行继续。如下：

```javascript
let longStr2 = 'This is a very very long string \
which needs to wrap across multiple lines \
because otherwise your code is unreadable.'
```

注意：在`\`之后不能有空格、注释和其它任何字符（换行符除外），否则它就不工作了。

### 5.2 输出多行字符串

可使用模板字符串，它支持直接换行。

```javascript
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 字符编码模型》](https://anjia1.gitbook.io/cs-foundation/string/unicode)。

## 6. 主要参考

* <https://en.wikipedia.org/wiki/String_(computer_science)>
* <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String>
