JS 中的二进制世界


文 / 泽白

最近接触了一些二进制读写相关的内容,重新探究了部分二进制的操作。

序言

从某种意义上讲,计算机上所有类型的信息最终都将被编码为固定大小的整数。作为实际发送和接收的数据形态,整数会被填充进不同长度的数据类型,并在读取时被赋值和转换,因此对整数的填充长度和符号的理解是非常重要的。

回顾

计算机中的信息包括数据信息和控制信息,数据信息又可分为数值和非数值信息。非数值信息和控制信息涵盖了字母、各种控制符号、图形符号等,它们都以二进制编码方式存人计算机并得以处理,这种对字母和符号进行编码的二进制代码称为字符编码(character code)。主要相关概念有:

  • 字节:是计算机中存储数据的基本单元,一个 8 位的二进制数。比如以十六进制表示的 0x000x010xFF
  • 字符:抽象意义上的一个符号,是各种文字、标点符号、图形符号、数字等的总称
  • 字符集:是一组抽象的字符集合。各个国家和地区制定了各自语言所需的字符集。比如英文字符集 ASCII、简体中文字符集 GB2312、繁体中文字符集 BIG5、日文字符集 JIS
  • 字符编码:规定了每个字符分别用一个字节还是多个字节表示,及用哪个字节值来存储

实战

Arraybuffer / TypedArray / DataView

Arraybufferjs 中的内置对象,平常很少会用到,是用来表示和操作结构化的缓冲区数据。它是字节数组,js 中是没有表示单个字节单位的对象的,Arraybuffer 即是最小单位的内置对象。详细可看MDN 定义

1
2
const buffer = new ArrayBuffer(8);
console.log(buffer.byteLength); // 8

Arraybuffer 中的内容是不能直接操作的,需要通过 TypedArrayDataView 对象来操作。

TypedArray 对象描述了描述了一个底层的二进制数据缓冲区(binary data buffer)的一个类数组视图(view)。js 中是没有名为 TypedArray 的对象的,可以通过以下对象构造 TypedArray 对象

1
2
3
4
5
6
7
8
9
new Int8Array();
new Uint8Array();
new Uint8ClampedArray();
new Int16Array();
new Uint16Array();
new Int32Array();
new Uint32Array();
new Float32Array();
new Float64Array();

有了 TypedArray 就可以像操作数组一样操作 Arraybuffer

1
2
3
const buffer = new ArrayBuffer(8);
const int8Array = new Int8Array(buffer, 1, 4);
console.log(int8Array.length); // 4

Int8Array 原型对象上的方法基本和数组类似,例如:

1
2
3
4
5
6
7
8
Int8Array.prototype.entries();
Int8Array.prototype.every();
Int8Array.prototype.fill();
Int8Array.prototype.filter();
Int8Array.prototype.find();
Int8Array.prototype.findIndex();
Int8Array.prototype.forEach();
// ...

DataView 内置对象提供了和 TypedArray 对象类似的功能,可以直接通过 new DataView() 创建一个 DataView 视图,用来从二进制 Arraybuffer 对象中读写多种数值类型。

1
2
3
4
5
6
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setUint16(0, 255);
console.log(view.getUint16(0)); // 255
console.log(view.getUint8(0)); // 0
console.log(view.getUint8(1)); // 255

image.png

base64

Base64 是网络上最常见的用于传输 8 比特字节代码的编码方式之一,也是前端不会陌生的一大利器。

atob / btoa

atobbtoa 两个方法是原生的将字符和 Base64 编码转换的方法,兼容性也比较好,可以参考CanIUse: atob

1
2
3
4
const Str = 'Foo';
const BStr = btoa(Str);
console.log(BStr); // 'Rm9v'
console.log(atob(BStr)); // 'Foo'

转译步骤(粗略)

  1. Foo 三个字符的 ASCII 编码转换为二进制放入一个 24 位的缓冲区
  2. 每次取 6 位作为 Base64 索引值
  3. 通过索引值查表,即可得到 Base64 编码

image.png

小结

二进制确实距离平常的前端开发比较遥远,文件上传会涉猎一些,但更多的是通过表单直接将文件以相应的格式通过 http 请求发送出去,很少会操作文件数据的诉求,经过一些简单的回顾,这些基础知识还是比较有趣的。学海无涯~