对于JavaScript使用的是UCS-2还是UTF-16这个问题,有同学咨询,的Java老师也进行了小小的研究却没有发现一个权威的回答,以下仅供参考。
一、的BMP(BasicMultilingualPlane)
Unicode标识符通过一个明确的名字和一个整数来作为它的码位(codepoint).比如,“©”字符的码位可以用“版权标志”和U+00A9(0xA9,也可以写作十进制169)来表示。
Unicode字符分为17组平面,每个平面拥有2^16(65,536)个码位.有一些码位没有分配字符,也有一些码位被保留,成为私有的,也有一些码位是永远被保留的,作为无字符的标志。每一个码位都可以用16进制xy0000到xyFFFF来表示,这里的xy是表示一个16进制的值,从00到10。
这第一个位置(当xy是00的时候)被称为BMP(基本多文种平面,BasicMultilingualPlane)。它包含了常用的码位从U+0000到U+FFFF。
这里需要补充一点额外的平面知识,以及术语的表格。
引用自:wikipedia
其余16号平面(U+100000到U+10FFFF)称为补充的平面。这里我将不讨论它;只需要记住两个概念:BMP字符和非BMP字符,后者也被称为补充字符。
二、UCS-2和UTF-16之间的不同
UCS-2和UTF-16都是Unicode的字符编码方式。
UCS-2(2个字节的通用字符集)是一种固定长度的编码格式,只需要使用编码为16字节编码单元来表示码位。这样的表示结果将和UTF-16在0到0xFFFF(BMP)范围内大多数的结果一样。
UTF-16(16位Unicode转换格式)是对UCS-2的一个扩展,它允许表示比BMP范围内更多的字符。它是一种可变长度格式,它的每个码位能够使用1位或者2位16字节编码单元来表示。这种方式能够编码的码位在0到0x10FFFF之间。
比如,在UCS-2和UTF-16中,对于BMP字符U+00A9版权标志(©)都能被编码为:0x00A9。
这里补充一下UCS-2、UCS-4、BMP
CPU处理多字节数的方式分为:“大尾”(bigendian)和“小尾”(littleendian),简单的理解就是一个Unicode编码,比如6C49,写到文件里面6C49或者496C,两种方式,前者就叫“大尾”,后者就叫“小尾”。
UCS可以分为两种格式:UCS-2和UCS-4。UCS-2使用两个字节编码,UCS-4使用4个字节(实际只有31位,zui高位必须是0)编码。
转换关系:UCS-4中高两个字节为0的码位称为BMP;UCS-4的BMP去掉前面两个零字节就得到UCS-2;UCS-2加上两个零字节就得到UCS-4中的BMP。
三、代理对(Surrogatepairs)
对于BMP之外的字符,比如U+1D306四条线居中(其实不好翻译:tetragramforcentre,?),只能使用UTF-16中两个16字节来编码:0xD8340XDF06。这种就被称为代理对。值得注意的是一个代理对只代表一个单字符。
补充一下代理对的概念
实际上就是指上面的一个UTF-16编码,使用2个16字节来编码。那是因为一个UTF-16编码不够,然后就应该使用2个UTF-16编码来表示更多的字符。然后这样就会出现:之前2个字节的空间表示一个字符,就会变成4个字节的空间。所以就规定只有一定范围内使用2个UTF-16编码来表示一个字符,这样的方式就叫代理对,其余的依然使用2个字节来表示。
代理对中的第一个字符单元总是在0xD800到0xDBFF之间,称为高位代理或者顶部代理(highsurrogateorleadsurrogate,暂时这样,查到专业术语再翻译)。第二个字符单元总是处于0xDC00到0xDFFF之间,称为低位代理或者尾部代理(lowsurrogateortrailsurrogate)。
UCS-2是缺乏对代理对的支持的,所以要表示之前的字符需要使用2个分隔的字符。
四、码位(codepoints)和代理对(surrogatepairs)之间的转换
Section3.7ofTheUnicodeStandard3.0(pdf) 中定义了一个转换算法。
假设:一个码位C大于0xFFFF的编码使用代理对<H,L>来表示的公式为:
1.H = Math.floor((C - 0x10000) / 0x400) + 0xD800
2.L = (C - 0x10000) % 0x400 + 0xDC00
转换公式变换后,比如从代理对<H,L>转换成一个Unicode码位C,可以使用公式:
1.C = (H - 0xD800) * 0x400 + L - 0xDC00 + 0x10000
五、Ok,那么关于JavaScript的编码问题呢?
在ECMAScript中定义来怎样解释字符的问题.
在level3或者更高等级的实现中,遵循国际标准,与Unicode3.0标准或者更新的标准,以及ISO/IEC10646-1,和UCS-2或者UTF-16作为编码格式。如果采用的ISO/IEC10646-1自己未被指定,它被认定为BMP的自己,集合300(这里没懂)。如果没有采用其它的编码格式,那么将按照UTF-16进行编码。
换句话说,JavaScript引擎是允许使用UCS-2或者UTF-16进行编码的。
然后按照specificparts规定,认为引擎里面的编码需要一些UTF-16的知识。
当然,内部引擎对于大多数JavaScript开发者来说没有实际影响。对于更多有趣的发现JavaScript是如何考虑字符的中,有一段:
尽管在本文档的其它部分中,表示字符单元和文字字符将使用16位的无符号值,用来表示单个16位文本单元。Unicode字符将使用抽象的语言或印刷单元(可超过16位,因此可以由多个代码单元)来表示。码位可以用Unicode标准值来表示。一个组合字符序列的成分可以有个别“Unicode字符”,即使一个用户可能会认为整个序列是单个字符。
JavaScript使用单独字符来处理字符单元,然后人们通常认为是一组Unicode字符。当使用BMP范围外Unicode字符的时候,这样会有一些不好的结果。比如代理对使用2个字符单元组成:'?'.length==2,即使这里是只有一个Unicode字符。如果是字符,代理对将暴露一部分:'?'=='\uD834\uDF06'。
在这里你想到了什么呢?对于这种方式,至少是UCS-2的替代方式(不同的地方是,UCS-2不允许有代理字符,然后JavaScript字符串是这样做的)。
你可以认为它像UTF-16一样在工作,特别是分成两部分的方式是被允许的,代理的这种错误排序是被允许的,代理被暴露成了分离的字符。类似UCS-2的行为对整个语言更有影响,比如补充字符范围的正则表达式比那些支持UTF-16的语言要更难写。
代理对只是为了显示在浏览器中(layout的时候),对单个Unicode字符的重新组合。这发生在JavaScript引擎的影响范围之外。为了证明这个,你能在document.write()的时候分开写一个高位代理和地位代理字符.
1.document.write('\uD834');
2.
3.document.write('\uDF06');
在结束后也将被渲染成一个图案:?。
六、结论
JavaScript引擎内部是自由的使用UCS-2或者UTF-16。大多数引擎使用的是UTF-16,无论它们使用什么方式实现,它只是一个具体的实现,这不将影响到语言的特性。
关注极悦官方微信,获得全新资料,还可获得免费面试题哦。