Simonzhangs' blog Simonzhangs' blog
首页
  • 前端文章

    • HTML
    • CSS
    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • JS设计模式总结
  • 《Vue》
  • 《React》
  • 《TypeScript 从零实现 axios》
  • TypeScript
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • apple music
  • extension
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Simonzhangs

前端学习探索者
首页
  • 前端文章

    • HTML
    • CSS
    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • JS设计模式总结
  • 《Vue》
  • 《React》
  • 《TypeScript 从零实现 axios》
  • TypeScript
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • apple music
  • extension
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

  • GitHub技巧

  • Nodejs

  • 博客搭建

  • Ajax

  • 计算机网络

  • 计算机编译原理

  • 涨知识

    • JavaScript的字符编码
      • 一、字符集
        • ASCII 字符集
        • Unicode 字符集
      • 二、字符编码
        • UTF-32 编码
        • UTF-8 编码
        • UTF-16 编码
      • 三、JavaScript 编码
        • JS 采用哪一种编码
        • 为什么 JS 选择已经淘汰的 UCS-2 编码方式
        • JS 字符函数的局限
        • 1. 正确识别字符串
        • 2. 码点表示法
        • 3. 字符串处理函数
        • 4. 正则表达式
    • 包装对象
    • Web页面的生命周期
  • 技术
  • 涨知识
simonzhangs
2022-05-07
目录

JavaScript的字符编码

# 一、字符集

各个国家文字、字母、标点符号等等的集合,成为字符集。

# ASCII 字符集

ASCII 字符集:计算机最初流行在欧美国家,针对他们的字符集合,主要是拉丁字母和阿拉伯数字。一共规定了 128 个字符以及对应的二进制转换关系,128 个字符包括了可现实的 26 个大小写字母、10 个数字、标点符号以及特殊的控制符。这 128 个字符用一个字节(8bit)来表示绰绰有余,一个字节可以表示 256 个字符,所以 ASCII 编码当前只利用了字节的 7 为,最高位统一置 0。

ASCII字符集

# Unicode 字符集

Unicode 字符集:ASCII 字符集具有很大的局限性,只能诸多表示 127 个字符,而没有考虑汉字、阿拉伯语等文字字符。Unicode 是全世界所有字符的一个集合。每个字符对应一个数字,每个数字对应一个字符,而这个数字也称之为码点,根据码点就可以从字符集中索引到对应的字符。

Unicode 字符集将全世界所有的字符包含在了一个集合里面,可以近似认为其中三分之二以上都来自东亚文字。这么多符号,Unicode 不是一次性定义的,而是分区定义。每个区可以存放 65536 个(2^16)字符,称为一个平面(plane)。目前,一共有 17 个(2^5)平面,也就是说,整个 Unicode 字符集的大小现在是 2^21。

最前面的 65536 个字符位,称为基本平面(缩写 BMP),它的码点范围是从 0 一直到 2^16-1,写成 16 进制就是从 U+0000 到 U+FFFF。所有最常见的字符都放在这个平面,这是 Unicode 最先定义和公布的一个平面。

剩下的字符都放在辅助平面(缩写 SMP),码点范围从 U+010000 一直到 U+10FFFF。

# 二、字符编码

先来说一下什么是字符编码呢?我们计算机传输和存储的数据都是二进制的,对于字符也是如此。把一个数值与字符集中的字符进行唯一匹配,对应的规则就称为字符编码。上面提到的字符集,会有一套与二进制数值对应的规则,即为对应的 ASCII 编码、Unicode 编码等等。

Unicode 编码方式的优点是它涵盖了所有的字符集合,缺点是因为涵盖了所有的字符集合,所有它相比于 ASCII 编码占用的字节多。比如说 ASCII 编码中一个字符只占 1 字节,而同样的字符在 Unicode 编码中至少占据 3 个字节,而我们并不会用的 Unicode 中所有的字符,常用的字符都分布在一个平面上,直接使用 Unicode 来表示字符就太浪费存储空间和传输流量了。

于是便出现了 UTF-8、16、32 等编码方式(Unicode Transformation Format),用来平衡 Unicode 所有平面字符的存储空间、兼容和解码问题。我们常见的 UTF-8 就是一种字符编码方式,它是针对 Unicode 的可变长度字符编码,也是一种前缀码。它可以用来表示 Unicode 标准中的任何字符,且其编码中的第一个字节仍然兼容 ASCII 字符集。

提示

Unicode 只规定了每个字符的码点,到底用什么样的字节序列表示这个码点,就涉及到了编码方法。对 Unicode 编码的方法有 UTF--8、UTF-16 和 UTF-32。

# UTF-32 编码

这是最直观的编码方法,每个码点都使用 4 个字节来表示,字节内容与码点一一对应,这就是 UTF-32。

比如说:码点 0 就用四个字节的 0 表示,码点 597D 就在前面加两个字节的 0。

U+0000 = 0x0000 0000

U+597D = 0x0000 597D
1
2
3

特点:

  • 查找效率高,时间复杂度 o(1);
  • 浪费空间,比相同的 ACII 编码文件大四倍;
  • HTML5 标准明文规定,网页不得编码成 UTF-32。

# UTF-8 编码

UTF-8 编码方式可以节省空间,它是变长的编码方法,字符长度从 1 一个字节到 4 个字节不等。越是常用的字符,字节越短,最前面的 128 个字符,只使用 1 个字节表示,与 ASCII 码完全相同。

UTF-8 编码方式:

  • 对于单字节字符,字节第一位置零,后 7 位使用该字符 Unicode 码点。即该字符编码同 ASCII 码一致
  • 对于 n(n > 1)个字节字符,最高位字节,前 n 位置 1,第 n + 1 位置 0,剩余低字节均以 10 开头,有效的二进制位(下图中的 x 占用位)则表示该字符的 Unicode 码点。
Unicode 码点范围(十六进制) UTF-8 编码方式(二进制) 字节数
U+0000 U+007F(0~127) 0xxx xxxx 单字节
U+0080 U+07FF(128~2047) 110x xxxx 10xx xxxx 双字节
U+0800 U+FFFF(2048~65535) 1110 xxxx 10xx xxxx 10xx xxxx 三字节
U+10000 U+10FFFF(65536~2097151) 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx 四字节

# UTF-16 编码

UTF-16 编码介于 UTF-32 与 UTF-8 之间,同时结合了定长和变长两种编码方法的特点。

它的编码规则:基本平面的字符占用 2 个字节,辅助平面的字符占用 4 个字节。即 UTF-16 的编码长度要么是 2 个字节(U+0000 到 U+FFFF),要么是 4 个字节(U+010000 到 U+10FFFF)

当 Unicode 码点在 U+0000 到 U+FFFF 范围内,则可以判断为基本平面字符,直接将码点转为对应的十六进制形式,长度为 2 个字节;

当 Unicode 码点在 U+010000 到 U+10FFFF范围内,则判断为辅助平面字符,会编码为4个字节。转换的规则是什么样的呢?

在基本平面内,从U+D800到U+DFFF是一个空段,即这些码点不对应任何字符。因此,这个空段可以用来映射辅助平面的字符。

具体来说,辅助平面的字符位共有2^20个,也就是说,对应这些字符至少需要20个二进制位。UTF-16将这20位拆成两半,前10位映射在U+D800到U+DBFF(空间大小210),称为高位(H),后10位映射在U+DC00到U+DFFF(空间大小210),称为低位(L)。这意味着,一个辅助平面的字符,被拆成两个基本平面的字符表示。

所以,当我们遇到两个字节,发现它的码点在U+D800到U+DBFF之间,就可以断定,紧跟在后面的两个字节的码点,应该在U+DC00到U+DFFF之间,这四个字节必须放在一起解读。

# 三、JavaScript 编码

# JS 采用哪一种编码

JavaScript 语言采用 Unicode 字符集,但是只支持一种编码方法。这种编码不是 UTF-16、8、32,用的是 UCS-2!

因此有两个团队都想来搞统一字符集,一个是 Unicode,另外一个就是 UCS-2 字符集;当他们发现彼此之后,决定合并成一套字符集,就是 Unicode。但是呢,UCS 的开发速度快于 Unicode,开发了第一套编码方法 UCS-2,使用 2 个字节表示已经有的码点字符。

而 Unicode 的编码方式之一 UTF-16 编码晚于 UCS-2 编码方法,即 UTF-16 宣布是 UCS-2 的超集;即 UTF-16 取代了 UCS-2,也可以说 UCS-2 被整合进了 UTF-16。

# 为什么 JS 选择已经淘汰的 UCS-2 编码方式

为什么 JavaScript 不选用更高级的 UTF-16 呢?因为 JavaScript 语言出现的以后,根本没有 UTF-16 编码,JS 语言发明者只用了 10 天设计了 JavaScript 语言,随后出现了解释引擎。

# JS 字符函数的局限

因为 JS 采用 Unicode 字符集,但是只支持 16 位的 UTF-16 编码,不支持 32 位。

由于 JavaScript 只能处理 UCS-2 编码,造成所有字符在这门语言中都是 2 个字节,如果是 4 个字节的字符,会被当作 2 个双字节的字符处理。JavaScript 的字符函数都受到了这一点的影响,无法返回正确结果。

为了解决这个问题,必须对码点做一个判断,然后手动调整。下面是正确遍历字符串的方法:

while(++index <length) {
  if(charCode >= 0xD800 && charCode <= 0xDBFF) {
    output.push(character + String.charAt(++index));
  }else {
    output.push(character)
  }
}
1
2
3
4
5
6
7

但在ES6版本,大幅增强了Unicode的支持,基本上解决了这个问题。

# 1. 正确识别字符串

ES6可以自动识别4字节的码点,可以通过for...of来遍历字符串:

let str = 's🏁s';
for(let s of str) {
  console.log(s,s.length)
}
// s 1
// 🏁 2
// s 1
1
2
3
4
5
6
7

可以看到for...of可以正常遍历字符串,即使是占4个字节也可以正常遍历;但是为了保持兼容,length属性仍然还是原来的行为。为了得到字符串的正确长度,可以用以下方式:

console.log(str.length)  // 4
console.log(Array.from(str1).length)  // 3
1
2

# 2. 码点表示法

JavaScript允许直接用码点表示Unicode字符,写法是"反斜杠+u+码点"。

'好' === '\u597D' // true
1

但是,这种表示法对4字节的码点无效。ES6修正了这个问题,只要将码点放在大括号内,就能正确识别。或者用UTF-16编码的四个字节比较:

'😂' === '\u1f602'  //false
'😂' === '\u{1f602}'  //true

'😂'.charCodeAt(0)  //55357
55357 .toString(16)  //'d83d'
'😂'.charCodeAt(1)  //56834
56834 .toString(16)  //'de02'
'😂' === '\ud83d\ude02'  //true
1
2
3
4
5
6
7
8

# 3. 字符串处理函数

ES6新增了几个专门处理4字节码点的函数。

  • String.fromCodePoint():从Unicode码点返回对应字符
  • String.prototype.codePointAt():从字符返回对应的码点
  • String.prototype.at():返回字符串给定位置的字符

# 4. 正则表达式

ES6提供了u修饰符,对正则表达式添加4字节码点的支持。


参考文章:

Unicode 与 JavaScript 详解 (opens new window)

编辑 (opens new window)
上次更新: 2022/06/16, 10:52:37
the-super-tiny-compiler解析
包装对象

← the-super-tiny-compiler解析 包装对象→

最近更新
01
一些有意思的类比
06-16
02
the-super-tiny-compiler解析
06-06
03
计算机编译原理总概
06-06
更多文章>
Theme by Vdoing | Copyright © 2021-2022
蜀ICP备2021023197号-2
Simonzhans | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
  • 飙升榜
  • 新歌榜
  • 云音乐民谣榜
  • 美国Billboard榜
  • UK排行榜周榜