在 ECMAScript 6 之前,JavaScript 對每個字元都是按照 16 位編碼的(UTF-16)處理的。即預設每個字元在計算機底層都是由 16 個 0 和 1 的序列組成。 一個這樣的 16 位序列稱一個 編碼單元(code unit)。
像字串的 length
屬性和 charAt()
方法都是基於 16 位編碼單元進行處理的。
但隨著 Unicode 字符集 的不斷擴充套件,0x0000
~0xFFFF
這個區間範圍,不足以表示所有字元了。這時再使用字串的 length
屬性和 charAt()
方法就存在問題了。
碼點
- 碼點(Code Points)就是字元編碼,用一個數字表示一個字元。
- 碼點既能表示
0x0000
~0xFFFF
範圍的字元,也能表示> 0xFFFF
範圍之外的字元。 - 碼點在計算機底層由 1 個或 2 個編碼單元組成。
BMP
0x0000
~0xFFFF
區間範圍,稱為 Basic Multilingual Plane (BMP)。在 BMP 中(包括),一個字元唯一對應一個編碼單元(一個 16 位二進位制序列)。
BMP 之外的區間稱為 supplementary planes。在 supplementary planes 中的每個字元,由 2 個編碼單元組成,稱 代理對(surrogate pairs)。
- 在
0x0000
~0xFFFF
區間範圍,一個碼點等於一個編碼單元。 - 在
> 0xFFFF
區間範圍,一個碼點等於兩個編碼單元。
老方法的問題
在 ECMAScript 5 中,每個字元都被看做,由一個編碼單元組成。那麼,在處理 supplementary planes 中的字元時,就有問題了。
var text = "?";
console.log(text.length); // 2
console.log(/^.$/.test(text)); // false
console.log(text.charAt(0)); // ""
console.log(text.charAt(1)); // ""
console.log(text.charCodeAt(0)); // 55362
console.log(text.charCodeAt(1)); // 57271
複製程式碼
"?"
在計算機底層由兩個編碼單元組成,也就是由兩個 16 位編碼序列組成。而在 .length
屬性、charAt()
方法和 charCodeAt()
方法的世界觀裡,每個字元都是用一個 16 位編碼序列表示的。
所以,.length
屬性值是 2;charAt(0)
和 charAt(1)
其實取的是 "?"
這個字第一個編碼單元和第二個編碼單元所表示的字元;charCodeAt(0)
更不能取到正確的字元了。
本質上,charAt
和 charCodeAt
後面的數字是表示編碼單元的索引值。
從 charCode 到 codePoint
上面的例子裡,如果使用 codePointAt()
,就不存在問題了。
var text = "?a";
console.log(text.charCodeAt(0)); // 55362
console.log(text.charCodeAt(1)); // 57271
console.log(text.charCodeAt(2)); // 97
console.log(text.codePointAt(0)); // 134071
console.log(text.codePointAt(1)); // 57271
console.log(text.codePointAt(2)); // 97
複製程式碼
在 > 0xFFFF
區間範圍,字元編碼值(char code) 不再有效,碼點依舊有效。所以,我們要:
- 從
String.fromCharCode
遷移到String.fromCodePoint
- 從
string.charCodeAt
遷移到string.codePointAt
工具方法:is32Bit
我們可以寫一個工具方法,判斷一個字元是不是 BMP 之外的字元。
function is32Bit(c) {
return c.codePointAt(0) > 0xFFFF;
}
console.log(is32Bit("?")); // true
console.log(is32Bit("a")); // false
複製程式碼
擴充套件連線
- Universal Character Set characters, from wikipedia.org
- Character Code Charts, from unicode.org
參考連結
- Better Unicode Support, from “Understanding ES6”
(完)