charCodeAt與codePointAt的用法:
- 相同點(diǎn):
charCodeAt與codePointAt都是字符串實(shí)例上的方法,用途都是用來(lái)返回指定索引位字符的Unicode編碼。 - 不同點(diǎn):
charCodeAt與codePointAt匹配索引位的規(guī)則不一樣。charCodeAt是根據(jù)碼元來(lái)匹配,codePointAt是根據(jù)碼點(diǎn)來(lái)進(jìn)行匹配的。
JavaScript 內(nèi)部,字符以 UTF-16(字符用兩個(gè)字節(jié)或四個(gè)字節(jié)表示) 的格式儲(chǔ)存,碼點(diǎn)范圍介于U+0000到U+FFFF,每個(gè)字符固定為2個(gè)字節(jié),一個(gè)碼元。對(duì)于那些需要4個(gè)字節(jié)儲(chǔ)存的字符(Unicode 碼點(diǎn)大于0xFFFF的字符),兩個(gè)碼元,JavaScript 會(huì)認(rèn)為它們是兩個(gè)字符。
在計(jì)算機(jī)發(fā)展的早期,由于存儲(chǔ)空間寶貴,Unicode使用16位二進(jìn)制來(lái)存儲(chǔ)文字。也就是一個(gè)碼元來(lái)存儲(chǔ)一個(gè)文字。
由于技術(shù)的發(fā)展,Unicode對(duì)文字編碼進(jìn)行了擴(kuò)展,將某些文字?jǐn)U展到了32位(占用兩個(gè)碼元),而一個(gè)文字對(duì)應(yīng)的二進(jìn)制數(shù)字是一個(gè)碼點(diǎn),所以使用32位二進(jìn)制數(shù)字存儲(chǔ)的文字(一個(gè)碼點(diǎn)=兩個(gè)碼元)
特別要注意,碼點(diǎn)可以是一個(gè)碼元,也可以是兩個(gè)碼元。
字符串的length屬性返回的是碼元。所以在對(duì)一些字符串如果要處理長(zhǎng)度的時(shí)候要注意這一點(diǎn)。
let str="A";
let strSpecial = "??";
console.log(str.length); // 1
console.log(strSpecial.length); // 2
(1)length屬性返回對(duì)應(yīng)幾個(gè)碼元字符
(2)str的Unicode是\u0041\只有一個(gè)字符,strSpecial Unicode編碼是\ud842\udfb7,雙字節(jié)兩個(gè)碼元,js識(shí)別為2個(gè)字符
let str="A";
let strSpecial = "??";
console.log(str.charCodeAt(0)); // 65
console.log(str.codePointAt(0)); // 65
console.log(strSpecial.charCodeAt(0)); // 55362
console.log(strSpecial.codePointAt(0)); // 134071
ES6 提供了codePointAt(0)方法,能夠正確處理 4 個(gè)字節(jié)儲(chǔ)存的字符,根據(jù)字符串碼元的位置得到其碼點(diǎn)。
- ??這個(gè)字的Unicode編碼是\ud842\udfb7,占用了兩個(gè)碼元。
- 所以當(dāng)用charCodeAt(0)是匹配0位的碼元,前兩個(gè)字節(jié)值 55362,toString(16)是ud842。
- 當(dāng)用codePointAt(0)是匹配0位的碼點(diǎn),codePointAt能識(shí)別出字符串的碼點(diǎn),所以反回134071。
codePointAt方法返回的是碼點(diǎn)的十進(jìn)制值,如果想要十六進(jìn)制的值,可以使用toString方法轉(zhuǎn)換一下。
let a = 'A';
let s = '??';
a.codePointAt(0).toString(16)
"41"
s.codePointAt(0).toString(16)
"20bb7"
但是這里需要注意參數(shù):
let strSpecial = "??";
console.log(strSpecial.charCodeAt(1));
console.log(strSpecial.codePointAt(0));
console.log(strSpecial.codePointAt(1));

codePointAt(1)與charCodeAt(1)的返回值相同,都是返回后兩個(gè)字節(jié)的碼元值。
這是因?yàn)樗饕皇歉鶕?jù)碼元,而匹配的規(guī)則是根據(jù)碼點(diǎn)的規(guī)則。如果后面兩位碼元是一個(gè)碼點(diǎn),就會(huì)當(dāng)作一個(gè)碼點(diǎn)來(lái)處理。
let str="A";
let strSpecial = "??A";
console.log(str.charCodeAt(0));
console.log(strSpecial.codePointAt(0));
console.log(strSpecial.codePointAt(1));
console.log(strSpecial.codePointAt(2));

strSpecial.codePointAt(1)取的不是A的字符值,是strSpecial后兩個(gè)字節(jié)的碼元值,而.codePointAt(2)才取得A的字符值,與str.charCodeAt(0)一樣。
解決順序問(wèn)題的配套辦法是使用for...of循環(huán),因?yàn)樗梢宰R(shí)別UTF-32(字符用四個(gè)字節(jié)表示,兩個(gè)碼元)
let strSpecial = "??";
for (let char of strSpecial) {
console.log(char.codePointAt(0));
}
關(guān)于for of可以參閱JavaScript for of 循環(huán)一章節(jié)。
codePointAt()方法是測(cè)試一個(gè)字符由兩個(gè)字節(jié)還是由四個(gè)字節(jié)組成的最簡(jiǎn)單方法。
function is32Bit(c) {
// 如果碼點(diǎn)大于了16位二進(jìn)制的最大值,則其是32位的
return c.codePointAt(0) > 0xFFFF;
}
is32Bit("??") // true
is32Bit("a") // false
有用的字符編碼筆記:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
歡迎大佬指點(diǎn)