# 字符串
## 概述
### 定義
字符串就是零個或多個排在一起的字符,放在單引號或雙引號之中。
```javascript
'abc'
"abc"
```
單引號字符串的內部,可以使用雙引號。雙引號字符串的內部,可以使用單引號。
```javascript
'key = "value"'
"It's a long journey"
```
上面兩個都是合法的字符串。
如果要在單引號字符串的內部,使用單引號,就必須在內部的單引號前面加上反斜杠,用來轉義。雙引號字符串內部使用雙引號,也是如此。
```javascript
'Did she say \'Hello\'?'
// "Did she say 'Hello'?"
"Did she say \"Hello\"?"
// "Did she say "Hello"?"
```
由于 HTML 語言的屬性值使用雙引號,所以很多項目約定 JavaScript 語言的字符串只使用單引號,本教程遵守這個約定。當然,只使用雙引號也完全可以。重要的是堅持使用一種風格,不要一會使用單引號表示字符串,一會又使用雙引號表示。
字符串默認只能寫在一行內,分成多行將會報錯。
```javascript
'a
b
c'
// SyntaxError: Unexpected token ILLEGAL
```
上面代碼將一個字符串分成三行,JavaScript 就會報錯。
如果長字符串必須分成多行,可以在每一行的尾部使用反斜杠。
```javascript
var longString = 'Long \
long \
long \
string';
longString
// "Long long long string"
```
上面代碼表示,加了反斜杠以后,原來寫在一行的字符串,可以分成多行書寫。但是,輸出的時候還是單行,效果與寫在同一行完全一樣。注意,反斜杠的后面必須是換行符,而不能有其他字符(比如空格),否則會報錯。
連接運算符(`+`)可以連接多個單行字符串,將長字符串拆成多行書寫,輸出的時候也是單行。
```javascript
var longString = 'Long '
+ 'long '
+ 'long '
+ 'string';
```
如果想輸出多行字符串,有一種利用多行注釋的變通方法。
```javascript
(function () { /*
line 1
line 2
line 3
*/}).toString().split('\n').slice(1, -1).join('\n')
// "line 1
// line 2
// line 3"
```
上面的例子中,輸出的字符串就是多行。
### 轉義
反斜杠(\)在字符串內有特殊含義,用來表示一些特殊字符,所以又稱為轉義符。
需要用反斜杠轉義的特殊字符,主要有下面這些。
- `\0` :null(`\u0000`)
- `\b` :后退鍵(`\u0008`)
- `\f` :換頁符(`\u000C`)
- `\n` :換行符(`\u000A`)
- `\r` :回車鍵(`\u000D`)
- `\t` :制表符(`\u0009`)
- `\v` :垂直制表符(`\u000B`)
- `\'` :單引號(`\u0027`)
- `\"` :雙引號(`\u0022`)
- `\\` :反斜杠(`\u005C`)
上面這些字符前面加上反斜杠,都表示特殊含義。
```javascript
console.log('1\n2')
// 1
// 2
```
上面代碼中,`\n`表示換行,輸出的時候就分成了兩行。
反斜杠還有三種特殊用法。
(1)`\HHH`
反斜杠后面緊跟三個八進制數(`000`到`377`),代表一個字符。`HHH`對應該字符的 Unicode 碼點,比如`\251`表示版權符號。顯然,這種方法只能輸出256種字符。
(2)`\xHH`
`\x`后面緊跟兩個十六進制數(`00`到`FF`),代表一個字符。`HH`對應該字符的 Unicode 碼點,比如`\xA9`表示版權符號。這種方法也只能輸出256種字符。
(3)`\uXXXX`
`\u`后面緊跟四個十六進制數(`0000`到`FFFF`),代表一個字符。`XXXX`對應該字符的 Unicode 碼點,比如`\u00A9`表示版權符號。
下面是這三種字符特殊寫法的例子。
```javascript
'\251' // "?"
'\xA9' // "?"
'\u00A9' // "?"
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
```
如果在非特殊字符前面使用反斜杠,則反斜杠會被省略。
```javascript
'\a'
// "a"
```
上面代碼中,`a`是一個正常字符,前面加反斜杠沒有特殊含義,反斜杠會被自動省略。
如果字符串的正常內容之中,需要包含反斜杠,則反斜杠前面需要再加一個反斜杠,用來對自身轉義。
```javascript
"Prev \\ Next"
// "Prev \ Next"
```
### 字符串與數組
字符串可以被視為字符數組,因此可以使用數組的方括號運算符,用來返回某個位置的字符(位置編號從0開始)。
```javascript
var s = 'hello';
s[0] // "h"
s[1] // "e"
s[4] // "o"
// 直接對字符串使用方括號運算符
'hello'[1] // "e"
```
如果方括號中的數字超過字符串的長度,或者方括號中根本不是數字,則返回`undefined`。
```javascript
'abc'[3] // undefined
'abc'[-1] // undefined
'abc'['x'] // undefined
```
但是,字符串與數組的相似性僅此而已。實際上,無法改變字符串之中的單個字符。
```javascript
var s = 'hello';
delete s[0];
s // "hello"
s[1] = 'a';
s // "hello"
s[5] = '!';
s // "hello"
```
上面代碼表示,字符串內部的單個字符無法改變和增刪,這些操作會默默地失敗。
### length 屬性
`length`屬性返回字符串的長度,該屬性也是無法改變的。
```javascript
var s = 'hello';
s.length // 5
s.length = 3;
s.length // 5
s.length = 7;
s.length // 5
```
上面代碼表示字符串的`length`屬性無法改變,但是不會報錯。
## 字符集
JavaScript 使用 Unicode 字符集。JavaScript 引擎內部,所有字符都用 Unicode 表示。
JavaScript 不僅以 Unicode 儲存字符,還允許直接在程序中使用 Unicode 碼點表示字符,即將字符寫成`\uxxxx`的形式,其中`xxxx`代表該字符的 Unicode 碼點。比如,`\u00A9`代表版權符號。
```javascript
var s = '\u00A9';
s // "?"
```
解析代碼的時候,JavaScript 會自動識別一個字符是字面形式表示,還是 Unicode 形式表示。輸出給用戶的時候,所有字符都會轉成字面形式。
```javascript
var f\u006F\u006F = 'abc';
foo // "abc"
```
上面代碼中,第一行的變量名`foo`是 Unicode 形式表示,第二行是字面形式表示。JavaScript 會自動識別。
我們還需要知道,每個字符在 JavaScript 內部都是以16位(即2個字節)的 UTF-16 格式儲存。也就是說,JavaScript 的單位字符長度固定為16位長度,即2個字節。
但是,UTF-16 有兩種長度:對于碼點在`U+0000`到`U+FFFF`之間的字符,長度為16位(即2個字節);對于碼點在`U+10000`到`U+10FFFF`之間的字符,長度為32位(即4個字節),而且前兩個字節在`0xD800`到`0xDBFF`之間,后兩個字節在`0xDC00`到`0xDFFF`之間。舉例來說,碼點`U+1D306`對應的字符為`??,`它寫成 UTF-16 就是`0xD834 0xDF06`。
JavaScript 對 UTF-16 的支持是不完整的,由于歷史原因,只支持兩字節的字符,不支持四字節的字符。這是因為 JavaScript 第一版發布的時候,Unicode 的碼點只編到`U+FFFF`,因此兩字節足夠表示了。后來,Unicode 納入的字符越來越多,出現了四字節的編碼。但是,JavaScript 的標準此時已經定型了,統一將字符長度限制在兩字節,導致無法識別四字節的字符。上一節的那個四字節字符`??`,瀏覽器會正確識別這是一個字符,但是 JavaScript 無法識別,會認為這是兩個字符。
```javascript
'??'.length // 2
```
上面代碼中,JavaScript 認為`??`的長度為2,而不是1。
總結一下,對于碼點在`U+10000`到`U+10FFFF`之間的字符,JavaScript 總是認為它們是兩個字符(`length`屬性為2)。所以處理的時候,必須把這一點考慮在內,也就是說,JavaScript 返回的字符串長度可能是不正確的。
## Base64 轉碼
有時,文本里面包含一些不可打印的符號,比如 ASCII 碼0到31的符號都無法打印出來,這時可以使用 Base64 編碼,將它們轉成可以打印的字符。另一個場景是,有時需要以文本格式傳遞二進制數據,那么也可以使用 Base64 編碼。
所謂 Base64 就是一種編碼方法,可以將任意值轉成 0~9、A~Z、a-z、`+`和`/`這64個字符組成的可打印字符。使用它的主要目的,不是為了加密,而是為了不出現特殊字符,簡化程序的處理。
JavaScript 原生提供兩個 Base64 相關的方法。
- `btoa()`:任意值轉為 Base64 編碼
- `atob()`:Base64 編碼轉為原來的值
```javascript
var string = 'Hello World!';
btoa(string) // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"
```
注意,這兩個方法不適合非 ASCII 碼的字符,會報錯。
```javascript
btoa('你好') // 報錯
```
要將非 ASCII 碼字符轉為 Base64 編碼,必須中間插入一個轉碼環節,再使用這兩個方法。
```javascript
function b64Encode(str) {
return btoa(encodeURIComponent(str));
}
function b64Decode(str) {
return decodeURIComponent(atob(str));
}
b64Encode('你好') // "JUU0JUJEJUEwJUU1JUE1JUJE"
b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"
```
## 參考鏈接
- Mathias Bynens, [JavaScript’s internal character encoding: UCS-2 or UTF-16?](http://mathiasbynens.be/notes/javascript-encoding)
- Mathias Bynens, [JavaScript has a Unicode problem](http://mathiasbynens.be/notes/javascript-unicode)
- Mozilla Developer Network, [Window.btoa](https://developer.mozilla.org/en-US/docs/Web/API/Window.btoa)
- 前言
- 入門篇
- 導論
- 歷史
- 基本語法
- 數據類型
- 概述
- null,undefined 和布爾值
- 數值
- 字符串
- 對象
- 函數
- 數組
- 運算符
- 算術運算符
- 比較運算符
- 布爾運算符
- 二進制位運算符
- 其他運算符,運算順序
- 語法專題
- 數據類型的轉換
- 錯誤處理機制
- 編程風格
- console 對象與控制臺
- 標準庫
- Object 對象
- 屬性描述對象
- Array 對象
- 包裝對象
- Boolean 對象
- Number 對象
- String 對象
- Math 對象
- Date 對象
- RegExp 對象
- JSON 對象
- 面向對象編程
- 實例對象與 new 命令
- this 關鍵字
- 對象的繼承
- Object 對象的相關方法
- 嚴格模式
- 異步操作
- 概述
- 定時器
- Promise 對象
- DOM
- 概述
- Node 接口
- NodeList 接口,HTMLCollection 接口
- ParentNode 接口,ChildNode 接口
- Document 節點
- Element 節點
- 屬性的操作
- Text 節點和 DocumentFragment 節點
- CSS 操作
- Mutation Observer API
- 事件
- EventTarget 接口
- 事件模型
- Event 對象
- 鼠標事件
- 鍵盤事件
- 進度事件
- 表單事件
- 觸摸事件
- 拖拉事件
- 其他常見事件
- GlobalEventHandlers 接口
- 瀏覽器模型
- 瀏覽器模型概述
- window 對象
- Navigator 對象,Screen 對象
- Cookie
- XMLHttpRequest 對象
- 同源限制
- CORS 通信
- Storage 接口
- History 對象
- Location 對象,URL 對象,URLSearchParams 對象
- ArrayBuffer 對象,Blob 對象
- File 對象,FileList 對象,FileReader 對象
- 表單,FormData 對象
- IndexedDB API
- Web Worker
- 附錄:網頁元素接口
- a
- img
- form
- input
- button
- option
- video,audio