Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lodash源码解读(10)-一个字符的length一定等于1吗 #10

Open
sevenCon opened this issue Jul 20, 2019 · 1 comment
Open

lodash源码解读(10)-一个字符的length一定等于1吗 #10

sevenCon opened this issue Jul 20, 2019 · 1 comment

Comments

@sevenCon
Copy link
Owner

sevenCon commented Jul 20, 2019

前言

中国有句成语, 叫见微知著. 意思是通过一点点细微的东西可以看到背后的很多东西.

在阅读代码的过程之中, 很多的知识点都需要MDN的文档来帮助理解, 那些仅仅见过几次, 但是却没有怎么理解的东西.比如前面几篇的关于Map,WeakMap, DataView的东西.就是一些基本知识的补充.

同样, 看到了以下的代码, 感觉自己进入了未知领域.

String的一些理解

/**
* Gets the size of an ASCII `string`.
*
* @private
* @param {string} string The string inspect.
* @returns {number} Returns the string size.
*/
var asciiSize = baseProperty('length');

看到以上的代码, 想到了几个问题

  • String类型的字符串, 最长是多少?
  • .length 是ASCII? 一个字节长度? String类型储存不是UTF-16的吗? 有什么关系吗?
  • js里面一个长度(String.prototype.length)对应物理储存空间的多少个字节?

如果还不能很有底气的回答上来, 那么证明js的一些基本的知识还没有巩固到位.

编码字符集, 字符编码表

console.log("𐐷".length);
// => 2

image

是不是有些奇怪, JavaScript的length,我们平常都用的比较多, 但是也有不灵的时候.

这是关于Unicode, 和UTF-16编码的问题.
首先说下什么是编码字符集, 字符编码表, 在现代的编码模型之中, 有5个层次, 其中, 编码字符集, 字符编码表分别是第2,3个层次的概念.

编码字符集, 规定了一个字符集里面的字符, 及其对应的坐标(也叫码位).一个字符所占用的码位称为码位值,
而字符集里面的字符, 映射到对应的坐标, 就叫编码字符集.

这里有个概念, 叫编码空间, 也就是包含了所有的字符的空间维度, 比如, Unicode的编码空间从U+0000到U+10FFFF,共有1,112,064个码位(code point)可用来映射字符.此外, 编码空间也可以用点, 行, 面的形式来描述.

而javascript用的UTF-16编码, 说的指的是编码方案, 是字符集(Unicode)的编码方案, 规定了采用16bit来储存1,112,064个码位.

问题是16位只能对应65536个码位, 怎么储存超过65536部分的字符呢?
这就是字符编码表的作用了.

字符编码表,(CEF:Character Encoding Form)
字符编码表, 就是通过规定一个码元一对一隐射到编码字符集, 一个最简单的字符编码表, 就是使用足够多的容量, 可以实现最简单的一对一映射. 但是, 这只是在对非CJK字符编码的时候, 有效,CJK文字每个文字都占据一个码位, 需要的码位远远超过一个固定的数, 而且需要考虑到文字拓展的问题, 不断有新的文字产生, 需要占据码位.

比如一个16bit的编码方案, 一对一映射的形式, 只能编码65536个码位. 所以需要实现Unicode编码的话, 就需要另辟蹊径了.

CJK字符编码, 中日韩系列的字符, 由单个字母组成一个单词, 编码字符集只需要储存单个字母, 而不需要储存单词,可以节省大量的码位.

UTF-16

所以, UTF-16面对Unicode怎么去编码的,
怎么去用16bit的数量, 把1,112,064个码位值隐射到16bit上面的?

Unicode

Unicode 有1,112,064个码位, 从U+0000到U+10FFFF, 可以分为BMP, 和非BMP

BMP, 第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP), 其他平面称为辅助平面(Supplementary Planes)

总的来说, 就是前面0-65536 个字符, 是常用的字符, 可以涵盖我们普通的时候, 比如我们中文里面的都是包括在BMP里面的, 而不包括的字符, 就如𐐷.

BMP: U+000000 - U+00FFFF, 基本平面, 在基本平面里面, 有一个范围规定是不映射码位的, 而用对编码方案的支持, 这个范围是U+D800 - U+DFFF, 共8 * 16 * 16=2048个字符

非BMP: U+010000 - U+10FFFF, 辅助平面

在面对一些非BMP的字符的时候, UTF-16对非BMP是使用32bit来表示的. 就是说, 𐐷 在UTF-16下是用2个16bit来表示. 这个也是为什么在打印𐐷.length===2的原因.

也是lodash之中函数定义去获取长度的属性, 叫asciiSize的原因,因为UTF-16, 在JavaScript中, ascii字符也是16bit/length.

非BMP字符的UTF-16映射规则

上面说道UTF-16在表示非BMP字符的时候, 是用2个16bit, 也就是用2个字符长度32bit, 去表示Unicode里面的一个非BMP字符. 32bit 称作代理对, 区分前导代理, 后尾代理.

规则是这样的:

  • 用这个32bit的值减去U+10000, 得到一个20bit的值. 为什么减去10000后悔得到20bit的值呢, 因为32bit中, 最高的8bit, 是没有隐射到的.只需要24位就可以隐射U+10FFFF的码位了. 所以减去BMP的码位后得到20bit
  • 20bit中, 高10位, 低10bit, 分别是前导代理(lead surrogates), 和后尾代理(trail surrogates). 他们的范围只有2 ** 10 = U+3FF, 这也是只需要2048个位置就能够隐射所有非BMP字符的原因.
  • 前导代理的值 + 0xD800, 后尾代理的值 + U+DC00, 然后合并, 就是对应的UTF-16编码方式了

然后, JavaScript在检查字符的时候, 去按照16bit一个单位去寻找, 发现这个地址在U+D800 - U+DFFF内, 那么就可以确定, 他是32bit的非BMP字符, 会再去取下一个16bit的字符, 还原码位, 再合并, 去Unicode里面寻找字符值.

image

小结

  • 上述的第一个问题, String的为理论最大长度是2^53-1, 即是Math.MAX_VALUE. 实际这个值是在计算运行环境, 内存, V8的js引擎变量heap大小限制, 单个字符串的上限等等条件下, 是创建不出来的.
  • 上述的第二个问题, UTF-16编码方式是对Unicode的隐射规则, 那么前128个码位就是16bit的, 保存了对应的ASCII的值.计算.length的时候, 计算的单个码位的长度, 也就是单个非BMP字符会出现长度为2的情况
  • 上述的第三个问题, 一个length对应16bit, 2个字节.

参考链接: https://zh.wikipedia.org/wiki/UTF-16#UTF-16%E7%9A%84%E7%B7%A8%E7%A2%BC%E6%A8%A1%E5%BC%8F
https://zh.wikipedia.org/wiki/%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81
注: 本文的github地址为:lodash源码解读(10)-String.prototype.length 一定等于1吗 如有版权问题,或其他问题, 请给作者留言,感谢!

@sevenCon sevenCon changed the title lodash源码解读(10)-String类型的一些认识 lodash源码解读(10)-String.prototype.length 一定等于1吗 Jul 20, 2019
@sevenCon sevenCon changed the title lodash源码解读(10)-String.prototype.length 一定等于1吗 lodash源码解读(10)-一个字符的length一定等于1吗 Jul 28, 2019
@sevenCon
Copy link
Owner Author

sevenCon commented Aug 10, 2019

UTF-16可以支持Uncode编码的字符集增加到多少的时候不能用了?

utf-16 BMP 基本面用来做映射的只有2048个数, 也就是当码位值超过24位, 这时减去BMP的16位的时候, 得出的码位值大于20位, 那么就不能分拆成2位10位的数组. 所以当前 UTF-16支持的码位是 65536+16*65536 = 1114112个字符. 当Unicode的字符集超出这个范围的时候, 就不能用UTF-16了.可能只能用UTF-32.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant