'𠮷'.length为什么是2Array.from('𠮷').length为什么是1
前置知识----计算机基础知识
众所周知,计算机底层的所有数据都是以010101的二进制形式存在
(一)现有的进制
- 二进制: 0,1 表示 如:
Number.parseInt('11', 2) === 3 - 八进制: 以 8 为基数,第一个数字必须是零(0),然后是相应的八进制数字(数值 0~7)。如
Number.parseInt('070', 8) === 56 - 十进制:以10为基数,现实中几乎都是十进制,还记得学加法的逢10进1
- 十六进制 : 数值前缀 0x/0X,然后是十六进制数字
0~9以及A~F(不区分大小写)。Number.parseInt('0xA', 16) === Number.parseInt('0xa', 16) === 10
ps: Number.parseInt 解析字符串并返回指定基数的十进制整数
有了二进制,为什么要有8/10/16进制
更好的表示,你总不能写了一串的1010说这是10。二进制使用起来很不方便, 16/10/8进制可以解决这个问题。进制越大,数的表达长度也就越短。
如:十进制的256转化为二进制 parseInt(256).toString(2) === '100000000'
进制的转换原理
比如二进制 → 十六进制(取四合一法) 11010111 ==> 7D
-
0111 = ( 0 * 2^3) + (12^2) + (12^1) + (1*2^0) = 7;
-
1101 = ( 1 * 2^3) + (12^2) + (02^1) + (1*2^0) = D(13);
(二)计算机中的存储单位(位bit)
- 位 bit (比特)(Binary Digits):存放一位二进制数,即 0 或 1,最小的存储单位。
- 字节 byte:8个二进制位为一个字节(B),最常用的单位。 1B(bytes) = 8bit
- 1KB (Kilobyte 千字节)=1024B
- 1MB (Megabyte 兆字节 简称“兆”)=1024KB
- 1GB (Gigabyte 吉字节 又称“千兆”)=1024MB
- 1TB(Trillionbyte 万亿字节 太字节)=1024GB
(三) 常见字符编码
编码: 从一种格式转换为另一种格式,可以认为把0,1 翻译为你认识的字符,如英文,中文
ASCII 码
美国制定了一套字符编码,用单个字节对拉丁字母(英文字母)、阿拉伯数字(也就是 1234567890)、标点符号(,.!等)、特殊符号(@#$%^&等)以及一些具有控制功能(回车,换行)的字符与二进制位之间的关系,做了统一映射。这被称为 ASCII 码,一直沿用至今。共规定了128个字符的编码,比如空格SPACE是32(二进制00100000),大写的字母A是65(二进制01000001)
ps: 由于1个字节是8位,应该最多可以支持2^8 = 256种情况,其实ASCII 码高位始终为0.所以支持了2^7 = 128
标准的ASCII码就是基础的128位,而有些欧洲国家扩展了ASCII码的后128~255 被称为扩展的ASCII码
Unicode
为了解决乱码问题,Unicode把所有语言都统一到一套编码里,它从 0 开始,为每个符号指定一个独一无二编号,这叫做”码点”(code point)。比如,码点 0 的符号就是 null(表示所有二进制位都是 0)。
注意: Unicode 只是一个符号集,而非编码,中文大多数是两个字节的码点,它只规定了符号跟二进制代码的映射关系,却没有规定这个二进制代码应该如何存储
unicode的问题:
假设 Unicode 统一规定每个符号用2个字节存储, 如果把ASCII编码的A用Unicode编码,只需要在前面补0就可以,因此,A的Unicode编码是00000000 01000001,多了一倍的存储空间,在存储和传输上就十分不划算
UTF-8
它是一种变长的编码方式(减少存储跟传输 ), 是目前在互联网上使用最广的一种 Unicode 的实现方式。把一个Unicode字符根据不同的数字大小编码成1-4个字节,常用的英文字母被编码成1个字节,常用汉字通常是3个字节,对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的
UTF-32
固定4个字节表示一个字符
UTF-16
UTF-16结合了定长和变长两种编码方法,编码长度要么是2个字节(U+0000到U+FFFF),要么是4个字节(U+010000到U+10FFFF,英文被编码为2个字节,常用汉字通常是4个字节
JS 中的字符串编码
- JavaScript 字符串使用了两种 Unicode 编码混合的策略:UCS-2(使用2个字节表示已经有码点的字符)和 UTF-16。对于可以采用 16 位编码 (两个字节)的字符(U+0000~U+FFFF),这两种编码实际上是一样的
- 两者的关系简单说,就是UTF-16取代了UCS-2,或者说UCS-2整合进了UTF-16。所以,现在只有UTF-16,没有UCS-2**(历史缘由)**
ES5 字符串中存在的问题
只能正确识别Unicode码点小于0xFFFF的字符,造成如下问题:
(1)length属性的值与肉眼所见不相符
(2)for循环中以16位编码为unit而非字符,substring slice repalce 也有同样的问题
let text = String.fromCodePoint(0x20BB7)//'𠮷'
text.length //2 => (1)字符串长度与肉眼所见不相符
for (let i = 0; i < text.length; i++) {
console.log(text[i]); // 输出两个 [0]'\uD842' [1]'\uDFB7' => (2)循环错误
}
//(3)其他处理函数错误
text.substring(0,1) // '\uD842'
text.substring(0,2) // '𠮷'
// slice,repalce等的字符串方法的反应也是一样的 都只对2字节的码点有效
ES6对字符串的加强
原有的方法 String.prototype.charCodeAt(index) 返回0 到 65535(0xFFFF) 的UTF-16编码,如果是大于0xFFFF返回第一个编码单元
String.fromCodePoint(num1, /* …, */ numN):从Unicode码点返回对应字符, 可返回大于2字节的unicode码String.prototype.codePointAt(index):从字符返回对应的码点
let text = String.fromCodePoint(0x20BB7)//'𠮷'
text.codePointAt(0) //134071
text.codePointAt(0).toString(16) // '20bb7' 大于2字节的unicode码点
Array.from(text).length //1 => (1)正确的长度
for (let str of text) {
console.log(str); // 𠮷 => (2)循环正确
}
// (3) 将码点放入大括号,就能正确解读该字符
// 之前的双字节
"\uD842\uDFB7"
"\u20BB7" // '₻7' 输出有误
// 现在
"\u{20BB7}"// "𠮷"
// (4) 正则增加了u修饰符,支持4字节码点 /^.$/ 只有一个字符
console.log(/^.$/.test(text)) //false
console.log(/^.$/u.test(text) )// true
// (5) 带附加符号的表示
// 方法一(单个字符)
'\u01D1' // 'Ǒ'
// 方法二
'\u004F\u030C' // 'Ǒ'
// (6) 用 normalize方法修正
'\u01D1'==='\u004F\u030C' //false
'\u01D1'.normalize() === '\u004F\u030C'.normalize()
// true
扩展
js的字符字面量
- \xnn 以十六进制编码 nn 表示的字符(其中 n 是十六进制数字 0~F),例如\x41 等于"A"
- \unnnn 以十六进制编码 nnnn 表示的 Unicode 字符(其中 n 是十六进制数字 0~F),例如\u03a3 等于希腊字 符"Σ"
'一'.codePointAt(0) // 获取unicode码点 19968
parseInt(19968).toString(16) //十六进制'4e00'
console.log('\u4e00') // 一
console.log('\u002b;\x2b') // +;+
html字符的编码
html编码:
&#N;(十进制,N代表码点)或者&#xN;(十六进制,N代表码点)
<code>一</code> // 显示一 十进制
<code>一</code> // 显示一 十六进制
css字符编码
\nnnn以十六进制编码 nnnn 表示的 Unicode 字符(其中 n 是十六进制数字 0~F)
.div::before{
display: inline-block;
content: "\4e00";
}
如有问题欢迎指正