JavaScript中的编码

74 阅读6分钟

背景

工作中有时候遇到一些编码问题总是云里雾里,比如给get请求,给后端,后端同学有时候问为啥是%20呀,还有为啥不让用innerHTML呀,还有渲染小的图片为什么选base64呢。

概括

不同的语言都有一些自己的特殊字符,在传输数据的时候需要对字符编码,保证数据的正确性和安全性。

在JS中我们常遇到的是HTML编码,URL编码,JS里的字符编码。

咱们就从这几个方面入手了解,不过在这之前先了解一下编码

基础的编码

ASCII和Unicode

可以把它俩理解成一个定义了所有字符的表,ASCII定义的少,Unicode是一个庞大的字符集。

Unicode 的编码点是从 0x0 到 0x10FFFF,一共 1,114,112 个位置。一般用“U+”后面跟 16 进制的数值来表示一个 Unicode 字符,如 U+0020 表示空格,U+6C49 表示“汉”,等等。

UTF-8/-16/-32

ASCII和Unicode都是定义,没有规定具体的编码方式,这样就产生了UTF-8/-16/-32,来进行具体的编码。

示例编码过程(引用GPT-4o)

以字符 “A”(Unicode 编码 U+0041)、汉字“中”(Unicode 编码 U+4E2D)和表情符号“🌍”(Unicode 编码 U+1F30D)为例,来看一下 UTF-8 是如何编码的。

  1. 字符 “A”
    • Unicode 编码:U+0041
    • 范围:U+0000 到 U+007F,所以使用 1 字节。
    • UTF-8 编码:01000001(二进制)对应十六进制为 41
  2. 字符 “中”
    • Unicode 编码:U+4E2D
    • 范围:U+0800 到 U+FFFF,所以使用 3 字节。
    • UTF-8 编码:
      • 将 U+4E2D 转换为二进制:100111000101101
      • 填充到 3 字节格式中:11100100 10111000 10101101
      • 结果:E4 B8 AD
  3. 字符 “🌍”
    • Unicode 编码:U+1F30D
    • 范围:U+10000 到 U+10FFFF,所以使用 4 字节。
    • UTF-8 编码:
      • 将 U+1F30D 转换为二进制:00011111001100001101
      • 填充到 4 字节格式中:11110000 10011111 10001100 10001101
      • 结果:F0 9F 8C 8D

UTF-8/-16/-32的区别

编码方案变长/固定长度字节需求优点缺点适用场景
UTF-8变长(1-4字节)ASCII字符1字节,其他字符2-4字节兼容ASCII,节省空间,全球化支持多字节字符处理复杂网络传输、Web、文件系统
UTF-16变长(2或4字节)常用字符2字节,增补字符4字节多字节字符高效,全球化支持英文文本浪费空间,BOM问题系统级文本处理(Windows、Java等)
UTF-32固定长度(4字节)每字符4字节编码简单,字符定位快捷空间占用大,不适合网络传输特殊场合,字符查找和索引需求高

前端中的编码

  • HTML编码
  • URL编码
  • JS编码(UTF-16)

HTML,URL,JS都有一些特殊的字符,在编码的过程中,需要对这些特殊字符进行编码,这样程序才能正常的运行

HTML编码

< 和 > 用于定义标签,& 用于定义实体

HTML编码的核心是将特殊字符转为HTML实体表示。常见的HTML实体包括:

  • & 编码为 &
  • < 编码为 <
  • > 编码为 >
  • " 编码为 "
  • ' 编码为 '

URL编码

背景

字符限制: URL中只能使用一部分ASCII字符集中的字符,即0-9、a-z、A-Z、-、_、.、~ 等,其他字符都需要被编码。特殊字符(如?, &, =)在URL中具有独特的语法含义,不能直接用作数据的一部分。这一限制带来了数据传输上的不便,尤其是在传输包含空格、中文等非ASCII字符的数据时。

URL编码的核心是将特殊字符转为百分号编码形式,即 % 后跟两个十六进制数。这个转换规则基于字符的ASCII码。例如:

  • 空格字符(ASCII 32)编码为 %20
  • &字符(ASCII 38)编码为 %26
  • 非ASCII字符,例如汉字“中”,编码为 %E4%B8%AD

在JavaScript中,通过 encodeURIComponentdecodeURIComponent 方法可以编码和解码URL参数

Base64

Base64 要求把每三个 8Bit 的字节转换为四个 6Bit 的字节(38 = 46 = 24 ),然后把 6Bit 再添两位高位 0 ,组成四个 8Bit 的字节,也就是说,转换后的字符串理论上将要比原来的长 1/3 。

转换前 aaaaaabb ccccdddd eeffffff 转换后 00aaaaaa 00bbcccc 00ddddee 00ffffff

Base64 主要不是加密,它主要的用途是把一些二进制数转成普通字符用于网络传输。

Base64 就是从 ASCII 编码中挑选出 64 个字符和二进制一个字节 8bits 进行映射,这也就是 Base64 中 64 的含义。

为什么必须对二进制进行转换呢?这是因为互联网中的某些传输协议只支持某些特定的字符集,如果是其他的字符集是不支持的。比如说常用的发送电子邮件的附件。因为 SMTP 协议最开始设计的时候是支持 7 位 ASCII 字符,所以如果要传输文件的话,我们需要对文件进行编码之后再进行传输。

JS默认UTF-16

JavaScript 于 1995 年诞生,而在 1990 年代早期 Unicode 还处于发展的初期。最早的 Unicode 标准(1.0 和 1.1 版)设计为 16 位编码,能表示 65,536(2162^{16}216)个字符。这种 16 位字符编码即 UTF-16 的早期版本,也就是 JavaScript 诞生时的编码选择。

// 将字符串编码为UTF-8字节
const encoder = new TextEncoder();
const utf8Array = encoder.encode("Hello, 世界");

// 将UTF-8字节解码回字符串
const decoder = new TextDecoder("utf-8");
const decodedString = decoder.decode(utf8Array);

javascript: 和 data:

最后再提一下伪协议

  • data伪协议
  • javascript伪协议

它们都是用于在浏览器中执行特定操作或访问数据的URL方案。这些伪协议并不真正用于传输数据,而是通过浏览器的解释器执行特定的动作或访问数据。

data伪协议允许将数据直接嵌入到URL中,常用于在页面中嵌入小型资源。所以当浏览器遇到data:image/svg+xml;base64,PD94bxxxx会把它解析为资源

javascript: 伪协议用于执行JavaScript代码。当浏览器遇到以"javascript:"开头的URL时,会直接执行其后的JavaScript代码。

主要区别和用途

  • data: 伪协议主要用于将数据内联到URL中,常用于在页面中嵌入小型资源或者测试目的。
  • javascript: 伪协议用于执行JavaScript代码,可用于创建简单的交互效果、修改页面内容或执行其他DOM操作。

最后

文中有不对的地方欢迎大家指出。