對于JavaScript使用的是UCS-2還是UTF-16這個問題,有同學(xué)咨詢,動力節(jié)點的Java老師也進(jìn)行了小小的研究卻沒有發(fā)現(xiàn)一個權(quán)威的回答,以下僅供參考。
一、的BMP(BasicMultilingualPlane)
Unicode標(biāo)識符通過一個明確的名字和一個整數(shù)來作為它的碼位(codepoint).比如,“©”字符的碼位可以用“版權(quán)標(biāo)志”和U+00A9(0xA9,也可以寫作十進(jìn)制169)來表示。
Unicode字符分為17組平面,每個平面擁有2^16(65,536)個碼位.有一些碼位沒有分配字符,也有一些碼位被保留,成為私有的,也有一些碼位是永遠(yuǎn)被保留的,作為無字符的標(biāo)志。每一個碼位都可以用16進(jìn)制xy0000到xyFFFF來表示,這里的xy是表示一個16進(jìn)制的值,從00到10。
這第一個位置(當(dāng)xy是00的時候)被稱為BMP(基本多文種平面,BasicMultilingualPlane)。它包含了常用的碼位從U+0000到U+FFFF。
這里需要補(bǔ)充一點額外的平面知識,以及術(shù)語的表格。
引用自:wikipedia
其余16號平面(U+100000到U+10FFFF)稱為補(bǔ)充的平面。這里我將不討論它;只需要記住兩個概念:BMP字符和非BMP字符,后者也被稱為補(bǔ)充字符。
二、UCS-2和UTF-16之間的不同
UCS-2和UTF-16都是Unicode的字符編碼方式。
UCS-2(2個字節(jié)的通用字符集)是一種固定長度的編碼格式,只需要使用編碼為16字節(jié)編碼單元來表示碼位。這樣的表示結(jié)果將和UTF-16在0到0xFFFF(BMP)范圍內(nèi)大多數(shù)的結(jié)果一樣。
UTF-16(16位Unicode轉(zhuǎn)換格式)是對UCS-2的一個擴(kuò)展,它允許表示比BMP范圍內(nèi)更多的字符。它是一種可變長度格式,它的每個碼位能夠使用1位或者2位16字節(jié)編碼單元來表示。這種方式能夠編碼的碼位在0到0x10FFFF之間。
比如,在UCS-2和UTF-16中,對于BMP字符U+00A9版權(quán)標(biāo)志(©)都能被編碼為:0x00A9。
這里補(bǔ)充一下UCS-2、UCS-4、BMP
CPU處理多字節(jié)數(shù)的方式分為:“大尾”(bigendian)和“小尾”(littleendian),簡單的理解就是一個Unicode編碼,比如6C49,寫到文件里面6C49或者496C,兩種方式,前者就叫“大尾”,后者就叫“小尾”。
UCS可以分為兩種格式:UCS-2和UCS-4。UCS-2使用兩個字節(jié)編碼,UCS-4使用4個字節(jié)(實際只有31位,zui高位必須是0)編碼。
轉(zhuǎn)換關(guān)系:UCS-4中高兩個字節(jié)為0的碼位稱為BMP;UCS-4的BMP去掉前面兩個零字節(jié)就得到UCS-2;UCS-2加上兩個零字節(jié)就得到UCS-4中的BMP。
三、代理對(Surrogatepairs)
對于BMP之外的字符,比如U+1D306四條線居中(其實不好翻譯:tetragramforcentre,?),只能使用UTF-16中兩個16字節(jié)來編碼:0xD8340XDF06。這種就被稱為代理對。值得注意的是一個代理對只代表一個單字符。
補(bǔ)充一下代理對的概念
實際上就是指上面的一個UTF-16編碼,使用2個16字節(jié)來編碼。那是因為一個UTF-16編碼不夠,然后就應(yīng)該使用2個UTF-16編碼來表示更多的字符。然后這樣就會出現(xiàn):之前2個字節(jié)的空間表示一個字符,就會變成4個字節(jié)的空間。所以就規(guī)定只有一定范圍內(nèi)使用2個UTF-16編碼來表示一個字符,這樣的方式就叫代理對,其余的依然使用2個字節(jié)來表示。
代理對中的第一個字符單元總是在0xD800到0xDBFF之間,稱為高位代理或者頂部代理(highsurrogateorleadsurrogate,暫時這樣,查到專業(yè)術(shù)語再翻譯)。第二個字符單元總是處于0xDC00到0xDFFF之間,稱為低位代理或者尾部代理(lowsurrogateortrailsurrogate)。
UCS-2是缺乏對代理對的支持的,所以要表示之前的字符需要使用2個分隔的字符。
四、碼位(codepoints)和代理對(surrogatepairs)之間的轉(zhuǎn)換
Section3.7ofTheUnicodeStandard3.0(pdf) 中定義了一個轉(zhuǎn)換算法。
假設(shè):一個碼位C大于0xFFFF的編碼使用代理對<H,L>來表示的公式為:
1.H = Math.floor((C - 0x10000) / 0x400) + 0xD800
2.L = (C - 0x10000) % 0x400 + 0xDC00
轉(zhuǎn)換公式變換后,比如從代理對<H,L>轉(zhuǎn)換成一個Unicode碼位C,可以使用公式:
1.C = (H - 0xD800) * 0x400 + L - 0xDC00 + 0x10000
五、Ok,那么關(guān)于JavaScript的編碼問題呢?
在ECMAScript中定義來怎樣解釋字符的問題.
在level3或者更高等級的實現(xiàn)中,遵循國際標(biāo)準(zhǔn),與Unicode3.0標(biāo)準(zhǔn)或者更新的標(biāo)準(zhǔn),以及ISO/IEC10646-1,和UCS-2或者UTF-16作為編碼格式。如果采用的ISO/IEC10646-1自己未被指定,它被認(rèn)定為BMP的自己,集合300(這里沒懂)。如果沒有采用其它的編碼格式,那么將按照UTF-16進(jìn)行編碼。
換句話說,JavaScript引擎是允許使用UCS-2或者UTF-16進(jìn)行編碼的。
然后按照specificparts規(guī)定,認(rèn)為引擎里面的編碼需要一些UTF-16的知識。
當(dāng)然,內(nèi)部引擎對于大多數(shù)JavaScript開發(fā)者來說沒有實際影響。對于更多有趣的發(fā)現(xiàn)JavaScript是如何考慮字符的中,有一段:
盡管在本文檔的其它部分中,表示字符單元和文字字符將使用16位的無符號值,用來表示單個16位文本單元。Unicode字符將使用抽象的語言或印刷單元(可超過16位,因此可以由多個代碼單元)來表示。碼位可以用Unicode標(biāo)準(zhǔn)值來表示。一個組合字符序列的成分可以有個別“Unicode字符”,即使一個用戶可能會認(rèn)為整個序列是單個字符。
JavaScript使用單獨字符來處理字符單元,然后人們通常認(rèn)為是一組Unicode字符。當(dāng)使用BMP范圍外Unicode字符的時候,這樣會有一些不好的結(jié)果。比如代理對使用2個字符單元組成:'?'.length==2,即使這里是只有一個Unicode字符。如果是字符,代理對將暴露一部分:'?'=='\uD834\uDF06'。
在這里你想到了什么呢?對于這種方式,至少是UCS-2的替代方式(不同的地方是,UCS-2不允許有代理字符,然后JavaScript字符串是這樣做的)。
你可以認(rèn)為它像UTF-16一樣在工作,特別是分成兩部分的方式是被允許的,代理的這種錯誤排序是被允許的,代理被暴露成了分離的字符。類似UCS-2的行為對整個語言更有影響,比如補(bǔ)充字符范圍的正則表達(dá)式比那些支持UTF-16的語言要更難寫。
代理對只是為了顯示在瀏覽器中(layout的時候),對單個Unicode字符的重新組合。這發(fā)生在JavaScript引擎的影響范圍之外。為了證明這個,你能在document.write()的時候分開寫一個高位代理和地位代理字符.
1.document.write('\uD834');
2.
3.document.write('\uDF06');
在結(jié)束后也將被渲染成一個圖案:?。
六、結(jié)論
JavaScript引擎內(nèi)部是自由的使用UCS-2或者UTF-16。大多數(shù)引擎使用的是UTF-16,無論它們使用什么方式實現(xiàn),它只是一個具體的實現(xiàn),這不將影響到語言的特性。
關(guān)注動力節(jié)點官方微信,獲得全新Java視頻資料,還可獲得免費面試題哦。