JS 中的二进制世界

avatar
阿里巴巴 前端委员会智能化小组 @阿里巴巴

 文 / 泽白

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


序言


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


回顾


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


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


实战


Arraybuffer / TypedArray / DataView


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


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


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


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


new Int8Array();
new Uint8Array();
new Uint8ClampedArray();
new Int16Array();
new Uint16Array();
new Int32Array();
new Uint32Array();
new Float32Array();
new Float64Array();


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


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


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


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 对象中读写多种数值类型。


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


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 请求发送出去,很少会操作文件数据的诉求,经过一些简单的回顾,这些基础知识还是比较有趣的。学海无涯~