JavaScript使用哪一種編碼?
JavaScript語言采用Unicode字符集,但是只支持一種編碼方法。
這種編碼既不是UTF-16,也不是UTF-8,更不是UTF-32。上面那些編碼方法,JavaScript都不用。
JavaScript用的是UCS-2!
? ?UCS-2編碼
互聯(lián)網(wǎng)還沒出現(xiàn)的年代,曾經(jīng)有兩個團隊,不約而同想搞統(tǒng)一字符集。一個是1988年成立的Unicode團隊,另一個是1989年成立的UCS團隊。等到他們發(fā)現(xiàn)了對方的存在,很快就達成一致:世界上不需要兩套統(tǒng)一字符集。
1991年10月,兩個團隊決定合并字符集。也就是說,從今以后只發(fā)布一套字符集,就是Unicode,并且修訂此前發(fā)布的字符集,UCS的碼點將與Unicode完全一致。
UCS的開發(fā)進度快于Unicode,1990年就公布了第一套編碼方法UCS-2,使用2個字節(jié)表示已經(jīng)有碼點的字符。(那個時候只有一個平面,就是基本平面,所以2個字節(jié)就夠用了。)UTF-16編碼遲至1996年7月才公布,明確宣布是UCS-2的超集,即基本平面字符沿用UCS-2編碼,輔助平面字符定義了4個字節(jié)的表示方法。
?
兩者的關(guān)系簡單說,就是UTF-16取代了UCS-2,或者說UCS-2整合進了UTF-16。所以,現(xiàn)在只有UTF-16,沒有UCS-2。
? JavaScript的誕生背景
那么,為什么JavaScript不選擇更高級的UTF-16,而用了已經(jīng)被淘汰的UCS-2呢?
答案很簡單:非不想也,是不能也。因為在JavaScript語言出現(xiàn)的時候,還沒有UTF-16編碼。
1995年5月,Brendan Eich用了10天設(shè)計了JavaScript語言;10月,第一個解釋引擎問世;次年11月,Netscape正式向ECMA提交語言標準(整個過程詳見《JavaScript誕生記》)。對比UTF-16的發(fā)布時間(1996年7月),就會明白Netscape公司那時沒有其他選擇,只有UCS-2一種編碼方法可用!
JavaScript字符函數(shù)的局限
由于JavaScript只能處理UCS-2編碼,造成所有字符在這門語言中都是2個字節(jié),如果是4個字節(jié)的字符,會當(dāng)作兩個雙字節(jié)的字符處理。JavaScript的字符函數(shù)都受到這一點的影響,無法返回正確結(jié)果。
還是以字符為例,它的UTF-16編碼是4個字節(jié)的0xD834 DF06。問題就來了,4個字節(jié)的編碼不屬于UCS-2,JavaScript不認識,只會把它看作單獨的兩個字符U+D834和U+DF06。前面說過,這兩個碼點是空的,所以JavaScript會認為是兩個空字符組成的字符串!
上面代碼表示,JavaScript認為字符的長度是2,取到的第一個字符是空字符,取到的第一個字符的碼點是0xDB34。這些結(jié)果都不正確!
解決這個問題,必須對碼點做一個判斷,然后手動調(diào)整。下面是正確的遍歷字符串的寫法。
while (++index 《 length) {
// 。。。
if (charCode 》= 0xD800 && charCode 《= 0xDBFF) {
output.push(character + string.charAt(++index));
} else {
output.push(character);
}
}
上面代碼表示,遍歷字符串的時候,必須對碼點做一個判斷,只要落在0xD800到0xDBFF的區(qū)間,就要連同后面2個字節(jié)一起讀取。
類似的問題存在于所有的JavaScript字符操作函數(shù)。
String.prototype.replace()
String.prototype.substring()
String.prototype.slice()
。。。
上面的函數(shù)都只對2字節(jié)的碼點有效。要正確處理4字節(jié)的碼點,就必須逐一部署自己的版本,判斷一下當(dāng)前字符的碼點范圍。
評論
查看更多