字符的 Unicode 表示法
JavaScript 允許采用\uxxxx形式表示一個字符顽决,其中xxxx表示字符的 Unicode 碼點。
"\u0061"
// "a"
但是导匣,這種表示法只限于碼點在\u0000~\uFFFF之間的字符才菠。超出這個范圍的字符,必須用兩個雙字節(jié)的形式表示贡定。
"\uD842\uDFB7"
// "??"
"\u20BB7"
// " 7"
上面代碼表示赋访,如果直接在\u后面跟上超過0xFFFF的數(shù)值(比如\u20BB7),JavaScript 會理解成\u20BB+7。由于\u20BB是一個不可打印字符蚓耽,所以只會顯示一個空格渠牲,后面跟著一個7。
ES6 對這一點做出了改進(jìn)步悠,只要將碼點放入大括號签杈,就能正確解讀該字符。
"\u{20BB7}"
// "??"
"\u{41}\u{42}\u{43}"
// "ABC"
let hello = 123;
"\u{6F}";//o
hell\u{6F} // 123
'\u{1F680}' === '\uD83D\uDE80'
// true
上面代碼中鼎兽,最后一個例子表明答姥,大括號表示法與四字節(jié)的 UTF-16 編碼是等價的。
有了這種表示法之后谚咬,JavaScript 共有 6 種方法可以表示一個字符鹦付。
'\z' === 'z' // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true
codePointAt()
JavaScript 內(nèi)部,字符以 UTF-16 的格式儲存择卦,每個字符固定為2個字節(jié)敲长。對于那些需要4個字節(jié)儲存的字符(Unicode 碼點大于0xFFFF的字符),JavaScript 會認(rèn)為它們是兩個字符互捌。
var s = "??";
s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271
上面代碼中潘明,漢字“??”(注意,這個字不是“吉祥”的“吉”)的碼點是0x20BB7秕噪,UTF-16 編碼為0xD842 0xDFB7(十進(jìn)制為55362 57271)钳降,需要4個字節(jié)儲存。對于這種4個字節(jié)的字符腌巾,JavaScript 不能正確處理遂填,字符串長度會誤判為2,而且charAt方法無法讀取整個字符澈蝙,charCodeAt方法只能分別返回前兩個字節(jié)和后兩個字節(jié)的值吓坚。
ES6 提供了codePointAt方法,能夠正確處理 4 個字節(jié)儲存的字符灯荧,返回一個字符的碼點礁击。
let s = '??a';
s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.codePointAt(2) // 97
codePointAt方法的參數(shù),是字符在字符串中的位置(從 0 開始)逗载。上面代碼中哆窿,JavaScript 將“??a”視為三個字符,codePointAt 方法在第一個字符上厉斟,正確地識別了“??”挚躯,返回了它的十進(jìn)制碼點 134071(即十六進(jìn)制的20BB7)。在第二個字符(即“??”的后兩個字節(jié))和第三個字符“a”上擦秽,codePointAt方法的結(jié)果與charCodeAt方法相同码荔。
總之漩勤,codePointAt方法會正確返回 32 位的 UTF-16 字符的碼點。對于那些兩個字節(jié)儲存的常規(guī)字符缩搅,它的返回結(jié)果與charCodeAt方法相同越败。
codePointAt方法返回的是碼點的十進(jìn)制值,如果想要十六進(jìn)制的值誉己,可以使用toString方法轉(zhuǎn)換一下眉尸。
let s = '??a';
s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61"
你可能注意到了域蜗,codePointAt方法的參數(shù)巨双,仍然是不正確的。比如霉祸,上面代碼中筑累,字符a在字符串s的正確位置序號應(yīng)該是 1,但是必須向codePointAt方法傳入 2丝蹭。解決這個問題的一個辦法是使用for...of循環(huán)慢宗,因為它會正確識別 32 位的 UTF-16 字符。
let s = '??a';
for (let ch of s) {
console.log(ch.codePointAt(0).toString(16));
}
// 20bb7
// 61
codePointAt方法是測試一個字符由兩個字節(jié)還是由四個字節(jié)組成的最簡單方法奔穿。
function is32Bit(c) {
return c.codePointAt(0) > 0xFFFF;
}
is32Bit("??") // true
is32Bit("a") // false
String.fromCodePoint()
ES5 提供String.fromCharCode方法镜沽,用于從碼點返回對應(yīng)字符,但是這個方法不能識別 32 位的 UTF-16 字符(Unicode 編號大于0xFFFF)贱田。
String.fromCharCode(0x20BB7)
// "?"
上面代碼中缅茉,String.fromCharCode不能識別大于0xFFFF的碼點,所以0x20BB7就發(fā)生了溢出男摧,最高位2被舍棄了,最后返回碼點U+0BB7對應(yīng)的字符,而不是碼點U+20BB7對應(yīng)的字符殖侵。
ES6 提供了String.fromCodePoint方法彰檬,可以識別大于0xFFFF的字符,彌補了String.fromCharCode方法的不足乔询。在作用上樟插,正好與codePointAt方法相反。