我們對字串型別的資料並不會陌生。let str = 'value'
就是一個最簡單的生成字串例子。
我們還可以通過String(thing)
和new String(thing)
將任何型別的資料轉化成字串。
例如,把一個空物件傳入,String({})
返回的值為"[object Object]"
,這裡不得不提的就是,其他型別預設的toString()方法本質就是呼叫String({})
。
字串量能直接使用String
的屬性和方法,如'string'.toLowerCase()
。其實語言內部會將'string'
封裝成物件,再執行相應的方法,最後字串物件使用valueOf()
或toString()
方法得到字串的值。
屬性
常用屬性只有一個:length
,返回字串的長度。
方法
以下介紹的,除了String.raw()
為靜態方法,其他都是字串物件的方法。所有的字串方法都不會對原字串修改,都是返回一個新的字串或結果。
拼接/補全
- concat(string2, string3[, ..., stringN]):將一個或多個字串與原字串依次拼接,形成一個新的字串並返回。
- padStart(targetLength [, padString]):返回用給定的字串填充原字串的末尾,以達到指定長度。
padString
表示用於填充的字串,預設為" "
。 - padEnd(targetLength [, padString]):返回用給定的字串填充原字串的開頭,以達到指定長度。
padString
表示用於填充的字串,預設為" "
。 - repeat(count):返回一個用原字串重複拼接而成的新字串。
count
表示重複的次數。
查詢
- startsWith(searchString [, position]):布林值,判斷原字串是否是以另外一個給定的子字串“開頭”。
position
表示開始查詢的索引,預設為 0 。 - endsWith(searchString [, position]):布林值,判斷原字串是否是以另外一個給定的子字串“結尾”。
position
表示開始查詢的索引,預設為 0 。 - includes(searchString[, position]):布林值,判斷原字串是否包含給定的字串。
position
表示開始查詢的索引,預設為 0 。 - indexOf(searchValue[, fromIndex]):返回給定的字串在原字串的第一次出現的索引值,未找到返回 -1 。
fromIndex
表示開始查詢的索引。 - lastIndexOf(searchValue[, fromIndex]):與
indexOf()
類似,不同的是,返回給定的字串在原字串的最後一次出現的索引值。 - search(regexp):返回匹配給定正規表示式的索引值,未找到返回 -1 。
- match(regexp):檢索返回原字串匹配正規表示式的結果。
- matchAll(regexp):返回一個包含所有匹配正規表示式及分組捕獲結果的迭代器。
擷取
- slice(beginSlice[, endSlice]):返回從原字串擷取指定索引的片段。
beginSlice
表示作為開始字元的索引,endSlice
表示結束位置的索引,負數表示從後數起。 - substring(indexStart[, indexEnd]):與slice()基本一致,但
indexEnd
不能為負數。 - split([separator[, limit]]):使用指定的分隔符字串將原字串分割成字串陣列。
separator
表示作為分隔符的字串或正規表示式;limit
表示字串陣列的最大長度。 - trim():返回一個將原字元的兩端刪除空白字元。
- trimRight():返回一個將原字元的右端刪除空白字元。
- trimLeft():返回一個將原字元的左端刪除空白字元。
- chartAt(index):從原字串中返回指定索引的字元。
- replace(regexp|substr, newSubStr|function):返回原字串以特定規則被替換後的字串。當前字串不變。
轉換大小寫
- toLowerCase():返回一個將原字串轉化成小寫的字串。
- toUpperCase():返回一個將原字串轉化成大寫的字串。
字元碼點
- normalize([form]):按照指定的一種 Unicode 正規形式將當前字串正規化。
form
預設為 "NFC
"。例子:'\u01D1'.normalize() === '\u004F\u030C'.normalize(); // true
。 - charCodeAt():
內建迭代器([Symbol.iterator])
String
物件內建Iterator
物件,通過屬性[Symbol.iterator]
獲取,在遍歷時呼叫,如for...of
運算、展開運算子(...
)等。
獲取迭代器:
let strIterator = 'abcde'[Symbol.iterator]();
console.log(strIterator.next().value); // 'a'
console.log(strIterator.next().value); // 'b'
console.log(strIterator.next().value); // 'c'複製程式碼
修改迭代器:
// 必須建立字串物件
let str = new String('abcde');
// 原生迭代器效果
for (let i of str) {
console.log(i);
}
// 列印結果: a b c d e
// 自定義迭代器函式
str[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
}
// 測試自定義迭代器
console.log(...str); // 1 2 3複製程式碼
字串模板(Template literals)
字串模板用"反引號"表示,即 ` `
。
基本用法
模板字串內,字串量可以直接寫,${ }
內執行JavaScrip
程式碼。用於生成一個帶運算的字串結果。
let num1 = 123,
num2 = 456;
let temp = `hello ${num1 + num2} world !`;
console.log(temp); // "hello 579 world !"複製程式碼
標籤模板(Tagged templates)用法
我們一般呼叫函式的方式是fn(['hello ', 'world'], 999)
,函式名後用括號包裹引數。而標籤模板可以這樣寫:fn`hello ${args}world`
,函式名後直接跟``
。注意,這兩種方式的引數是一致的。
分解:
帶${ }
的模板字串,內部其實是被${
和}
分割成多份。以`hello ${999}world${666} !`
為例:
`
hello
${999}
world
${666}
!
`複製程式碼
${ }
的外面是字串直接量,而${ }
之間是 js 程式碼。如此看來,字串模板被分成了 5 份,而字串直接量會組成字串陣列,所以傳入函式時是 3 個引數,分別是['hello ', 'world', ' !']
、999
、666
。下面給出簡單例子說明。
// 用於檢視傳參情況的函式
function fn() {
console.log(arguments[0])
}
fn`hello ${999}world${666} !`;
// 下圖看結果複製程式碼
String.raw()
談到模板字串,不得不談String
物件的一個靜態方法:String.raw()。一般都是以標籤函式的方式呼叫它,即String.raw``
,當然,你可以使用普通函式的()
方式傳參,只不過要按照上面額規則手動轉化一下。它的用途是獲取一個模板字串的原始字面量值,簡單說就是把所有的反斜槓(\
)轉義。
String.raw `Hi\u000A!`; // "Hi\\u000A!"
String.raw `Hi\n${999}!`; // "Hi\\n999!"
String.raw`\\`; // "\\\\"
複製程式碼
字元編碼與字符集
基本概念
- 字元,是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。
- 字型檔表,是一個相當於所有可讀或者可顯示字元的資料庫,字型檔表決定了整個字符集能夠展現表示的所有字元的範圍。其實就是字元的集合。
- 編碼字符集,簡稱字符集,用一個編碼值
code point
(又稱碼點)來表示一個字元(即該字元在子庫表中的位置),這個值稱為字元對應於編碼字符集(如:Unicode、ASCII)的序號。 - 字元編碼,是編碼字符集和實際儲存數值之間的轉換關係。
用商場儲物櫃為例子。
字元,等於單個櫃箱,存放著我們想看的內容。
整個儲物櫃就是字型檔表。
為每個櫃箱按順序貼上標籤後,整個儲物櫃就是編碼字符集,每個數字(碼點)對應一個櫃箱(字元)。
我們手上有一份查詢表格,但表格上只能寫二進位制或十六進位制的數字。以什麼樣的方式把儲物櫃上的數字對應到表格上(對映),就是字元編碼。
ASCII
ASCII 既是字符集,也是字元編碼。用一個位元組的長度儲存字元。自行百度,不多介紹。
Unicode
Unicode 是一個字符集,為每個符號指定一個編號,即"碼點"(code point)。其目標是將全世界所有的字元包含在一個集合裡,計算機只要支援這一個字符集,就能顯示所有的字元。
每個區可以存放 65536 個(216)字元,稱為一個平面(plane)。
目前,一共有 17 個(25)平面,也就是說,整個 Unicode 字符集的大小現在是 221 。
最前面的 65536 個字元位,稱為基本平面(縮寫 BMP ),它的碼點範圍是從 0 一直到 216-1 ,寫成 16 進位制就是從 U+0000 ~ U+FFFF
。
剩下的字元都放在輔助平面(縮寫 SMP ),碼點範圍為 U+010000 ~ U+10FFFF
。
UTF-32
UTF-32 是字元編碼方式,用固定長度的 4 位元組表示一個字元,與 Unicode 位元組內容一一對應碼點。例如:
U+597D = 0x0000 597D複製程式碼
但缺點很明顯,浪費空間。HTML5 標準就明文規定,網頁不得編碼成UTF-32。
UTF-8
UTF-8 是一種變長的編碼方法,字元長度從 1 個位元組到 4 個位元組不等。越是常用的字元,位元組越短,最前面的 128 個字元,只使用 1 個位元組表示,與 ASCII 碼完全相同。如,編碼範圍在0x0000 - 0x007F
只佔用 1 位元組,而0x010000 - 0x10FFFF
要佔用 4 位元組。是最常見的網頁編碼。
由於 UTF-8 是變長的,若不直到其長度,連續的字元則無法解析。如何判斷其長度,這就涉及它的編碼規則:
- 對於單位元組的符號,第一位為 0 ,用二進位制表示為
0xxx xxxx
。 - 對於n位元組的符號(n>1),第一個位元組的前n位都設為1,第n+1位設為0,後面位元組的前兩位一律設為10。剩下的沒有提及的二進位制位,全部為這個符號的unicode碼。
用阮一峰的博文的圖表展示:
Unicode 符號範圍 (十六進位制) | UTF-8 編碼方式 (二進位制) | 位元組 |
---|---|---|
0000 0000-0000 007F | 0xxxxxxx | 1 |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx | 2 |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx | 3 |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | 4 |
圖表中x
組合而成的就是實際的 Unicode 碼點。
UTF-16
UTF-16 編碼介於 UTF-32 與 UTF-8 之間,同時結合了定長和變長兩種編碼方法的特點。編碼規則就是,基本平面的字元佔用 2 個位元組,輔助平面的字元佔用 4 個位元組。
因為在基本平面內,從U+D800 ~ U+DFFF
是一個空段,空間為 211 ,即 2 個 210 。而一個輔助平面的字元需要的空間為 220 ,剛好可以拆分成兩個空段內的字元,高位(H)對映在U+D800 ~ U+DBFF
;低位(L)對映在U+DC00 ~ U+DFFF
。因此解決了 4 位元組的字元的識別問題。
所以,對於基本平面,Unicode 與 UTF-16 碼相同。
對於輔助平面,Unicode 與 UTF-16 的轉化公式如下:
H = Math.floor((c-0x10000) / 0x400) + 0xD800
L = (c - 0x10000) % 0x400 + 0xDC00複製程式碼
不難理解,輔助平面的碼點是從U+10000
開始,則減去0x10000
;上面提到高低位各佔 10 位,則除以0x400
得到的商和餘數則分別位高低位的起點值,最後高位加上基礎值0xD800
,低位加上0xDC00
得到目標值。(內容總結自阮一峰的博文)
JavaScript 使用的編碼方式
JavaScript 語言採用 Unicode 字符集,但是隻支援一種編碼方法,就是 UCS-2 。由於歷史原因(阮一峰的博文有介紹),UCS-2 只支援 2 位元組的字元,4 位元組的字元被當成 2 個 2 位元組的字元解析。現在已經沒有 UCS-2 。
ES6 增強對 Unicode 的支援:
- ES6 可以自動識別 4 位元組的碼點,如
console.log('\ud834\udf06'); // ?
。 - 允許直接用碼點表示Unicode字元,如
'?' === '\u{1d306}'; // true
。2 位元組的字元可以省略{}
。 - ES6 新增了幾個專門處理 4 位元組碼點的函式(看上方)。
- ES6 提供了
u
修飾符,對正規表示式新增4位元組碼點的支援。