这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战。
前言
ECMAScript 6.0 简称ES6 , 是 JavaScript 语言的新一代的标准,于在 2015 年 6 月发布,正式名称就是《ECMAScript 2015 标准》。一般情况,泛指, 5.1版以后的标准,涵盖了 ES2015、ES2016、ES2017、ES2018、ES2019、ES2020、ES2021 等等
我们一起来学习字符串乱码 �!
为什么一定是�
U+D800
到U+DFFF
区间没有字符是没法打印的,故打印�。
上篇字符�之谜已经说了UTF-16的编码规则,和 "𢂘"编码后的结果。
var ch = "𢂘";
ch.charAt(0) // '\uD848'
ch.charAt(1) // '\uDC98
但是不是所有的码点大于0xFFFF的字符在UTF-16编码下,ch.charAt(0)和ch.charAt(1)都是乱码呢?
这个又的回到了码点大于0xFFFF字符编码过程:
- 码位减去
0x10000
,得到的值的范围为20比特长的0...0xFFFFF
,不足的话,前面补0喽。 - 高位的10比特的值加上
0xD800
得到第一个码元 - 低位的10比特的值加上
0xDC00
得到第二个码元
Unicode字符集的编码值范围为0 ~ 0x10FFFF
, 大于0xFFFF
的区间为 0x10000 ~0x10FFFF
减掉 0x10000
, 其值的区间范围为 0x0000 ~ 0xFFFFF
0x10000 ~ 0x10FFFF
0x10000 ~ 0x10000
------------------- 减掉 0x10000
0x00000 ~ 0xFFFFF
转为二进制
高10位 低10位
0000000000 0000000000 --- 0xF0000
1111111111 1111111111 --- 0xFFFFF
上10位的取值区间:0000000000 ~ 1111111111
对应16进制 0x0000 ~ 0x03ff
下10位的取值区间:0000000000 ~ 1111111111
对应16进制 0x0000 ~ 0x03ff
第一码元,上10位加上 0xD800
, 其值区间为:
0xD800 + 0x0000 ~ 0xD800 + 0x03ff
= 0xD800 ~ 0xDBFF
'0x' + (0xD800 + 0x0000).toString(16).toUpperCase() // 0xD800
'0x' + (0xD800 + 0x03ff).toString(16).toUpperCase() // 0xDBFF
第二码元,下10位加上 0xDC00
, 其值区间为:
0xDC00 + 0x0000 ~ 0xDC00 + 0x03ff
= 0xDC00 ~ 0xDFFF
'0x' + (0xDC00 + 0x0000).toString(16).toUpperCase() // 0xDC00
'0x' + (0xDC00 + 0x03ff).toString(16).toUpperCase() // 0xDFFF
简单总结一下:
第一码元值区间: 0xD800 ~ 0xDBFF
第二码元值区间: 0xDC00 ~ 0xDFFF
Unicode的代理区 0xD800
-0xDFFF
, 其不代表任何字符。
第一码元和第二码元的值,死死的卡在代理区,必然是乱码。 所以码点大于0xFFFF的字符。
- 其长度值为2
- 采用索引值取值,必然两个都是乱码
- charAt也必然是乱码
这下你应该知道了吧,为什么一定会是乱码。
0xD800
-0xDFFF
不知不说UTF-16编码的思路真的是妙不可言, 我这是顺方向推导。 其设计之初
的0xD800
与0xDC00
是怎么被推导出来的呢?
大于0xFFFF的码点,减去0x10000区间 0x0000 ~ 0xFFFFF
需要四字节保存,计算过程参考前面为什么一定是�
前四值区间: 0x0000 ~ 0x03ff
后四值区间: 0x0000 ~ 0x03ff
0x03ff // 1023
0xDFFF - 0xD800 // 2047
到这里就很有意思了,代理区可用的码点是2047,差不多刚好是0x03ff(1023)的两倍。
刚好前四个字节和后四个字节需要的码点是 2046个,
代理区 (最大值 - 最小值)/2 + 最小值
'0x' + ((0xDFFF - 0xD800) / 2 + 0xD800).toString(16).toUpperCase() // '0xDBFF.8'
这里就差不多明白了吧,我真的是个小机灵鬼。
小结
今天你收获了吗?