概述
本文主要通过对 JavaScript 中数字数据与二进制数据之间的转换, 让读者能够了解在 JavaScript 中如何对数字类型 (包括但不限于 Number 类型) 进行处理
二进制数据在日常的 JavaScript 中很少遇到, 但是当你使用 webSocket 与后端进行数据交互时, 就有可能会用到二进制的数据格式因此, 为了更好的理解本系列中之后发布的关于 WebSocket 传输二进制相关的内容, 我们有必要了解二进制数据在 JavaScript 中是如何进行操作和存储的
本文内容主要为:
JavaScript 中如何操作与存储二进制数据 ArrayBuffer 存储结构相关基础知识以及对应的 DataView 相关数据类型基础知识和和 API 接口, 同时对字节序问题进行介绍
以 Int 和 Short 为例, 说明 JavaScript 中的数字数据如何转换为二进制数据
以 Long 类型为例, 说明 JavaScript 中如何表示 Long 类型并且如何将其转换为二进制数据
如何将二进制数据中转换为 JavaScript 中的数字数据
本文与 WebSocket 并无太强关联, 不过作为在 WebSocket 中传递二进制数据的基础知识储备, 因此放入了此系列当中
如果读者对 WebSocket 并不了解, 或者说不明白它的使用场景和细节, 可以阅读我的前一篇博客 WebSocket 系列之基础知识入门篇
如果读者想了解 String 类型与二进制之间的处理和转换, 可以于都 WebSocket 系列稍后发布的文章(文章发布后会替换此段)
如果读者想了解在 WebSocket 中如何进行二进制的传递和解析, 可以阅读 WebSocket 系列稍后发布的文章(文章发布后会替换此段)
JavaScript 中如何存储和操作二进制数据
了解了为什么需要使用二进制数据, 我们来看下, 在 JavaScript 中如何存储和操作二进制数据
ArrayBuffer
首先, 我们要介绍下在 JavaScript 中用来存储二进制数据的 ArrayBuffer
ArrayBuffer 对象用来表示通用的固定长度的原始二进制数据缓冲区
在 MDN 的文档中, 我们能够看到 ArrayBuffer 的介绍它是在 JavaScript 中用来进行二进制数据存储的一种数据对象
下面我们通过一个示例来简单介绍下 ArrayBuffer 相关操作
- const buffer = new ArrayBuffer(8);
- buffer.byteLength; // 结果为 8
上面的示例通过创建一个长度为 8Byte 的二进制数据缓冲区缓冲区只是一个数据存储的空间, 如何对这个存储空间进行读取, 完全取决于使用者例如: 8 个字节可以当成是 2 个 Int 类型的数据, 也可以是一个 Long 类型的数据, 或者 4 个 Short 型的数据
DataView
看完了存储数据的 ArrayBuffer, 我们来看下数据读写的 DataView
DataView 视图是一个可以从 ArrayBuffer 对象中读写多种数值类型的底层接口, 在读写时不用考虑平台字节序问题
这个是在 MDN 中关于 DataView 的介绍 DataView 提供了大量的 API 接口来进行数据的读和写操作, 我们在后三章将会举例进行说明但是, 首先我们得先看下说明中提到的字节序问题
字节序
在现有的计算机体系中, 有两种字节序:
大端字节序: 高位在前, 低位在后符合人类阅读习惯
小端字节序: 低位在前, 高位在后符合计算机读取习惯
上面所说的顺序均是针对多字节对象而言, 如 Int 类型, Long 类型以 Int 类型数据
0x1234
为例, 如果是大端字节序, 那么数据从人类对数值的通常写法上来看就是
0x1234
; 如果是小端字节序, 那么从人类对数值的通常写法上来看, 应该写成
0x3412
对于单字节对象如 Byte 类型数据而言, 没有字节序一说
在不同的平台中, 可能使用不同的字节序, 这就是所谓的字节序问题 DataView 所谓的在读写时不需要考虑平台字节序问题是指: 同时使用 DataView 进行写入和读取的数据保持一致
JavaScript 中的数字数据如何转换为二进制数据
对 ArrayBuffer 和 DataView 有了一个大概的了解, 下面让我们来看下它是如何进行二进制数据操作的
本章, 我以 Short 类型和 Int 类型为例, 介绍下相关操作步骤
- let buffer = new ArrayBuffer(6); // 初始化 3 个 Byte 的二进制数据缓冲区
- let dataView = new DataView(buffer);
- dataView.setInt16(0, 3); // 从第 0 个 Byte 位置开始, 放置一个数字为 3 的 Short 类型数据(占 2 Byte)
- dataView.setInt32(2, 15); // 从第 2 个 Byte 位置开始, 放置一个数字为 15 的 Short 类型数据(占 4 Byte)
通过上面的示例, 我们一共初始化了 6 个 Byte 的存储空间, 使用 1 个 Short 类型 (占 2 Byte) 和一个 Int 类型 (占 4 Byte) 的数据进行填充
DataView 还提供了许多的 API 接口来进行其他数据类型的处理, 如无符号型, 浮点数等他们的使用方法和上面介绍的 API 相同, 我们在这里就不一一进行介绍了, 希望了解更多 API 接口的读者可以查看 MDN 文档
JavaScript 中如何表示 Long 类型并且如何将其转换为二进制数据
通过 DataView 提供的 API 接口, 我们知道了如何处理 Short 类型 Int 类型 Float 类型和 Double 类型那么, 如果是对于 Long 类型这种原生 API 中没有提供处理函数的数据类型, 我们应该如何处理呢?
首先, 我们需要理解 Long 数据类型的结构, 它是由一个高位的 4 个 Byte 和低位的 4 个 Byte 组成的数据类型因为 Long 类型表示的范围比 Number 类型大, 所以我们在 JavaScript 中是使用了两个 Number 类型 (即 Int 类型) 的对象来表示 Long 类型数据, 相关的具体细节可以见我之前的博客 Long.js 源码分析与学习
理解了 JavaScript 中如何存储 Long 类型, 我们就知道如果对其进行存储
- import Long from 'long';
- let long = Long.fromString('123');
- let buffer = new ArrayBuffer(8);
- let dataView = new DataView(buffer);
- dataView.setInt32(0, long.high); // 采用大端字节序放置
- dataView.setInt32(4, long.low);
通过上面的示例, 我们将一个 Long 类型的数据拆分成了两个 Int 类型的数据, 按照大端字节序放入到了 ArrayBuffer 中同理, 如果是想按照小端字节序放置, 只需要将数据进行部分处理后再放入即可, 在此我就不过多介绍了
如何将二进制数据中转换为 JavaScript 中的数据类型
当你知道了如何将数据转换为 ArrayBuffer 中存储的二进制数据后, 就能够简单推测出如何进行反向操作将数据从 ArrayBuffer 中读取出来, 再转换成 JavaScript 中常用数据类型
- import Long from 'long';
- let buffer = new ArrayBuffer(14); // 初始化 3 个 Byte 的二进制数据缓冲区
- let dataView = new DataView(buffer);
- let long = Long.fromString('123');
- // 数据写入过程
- dataView.setInt16(0, 3); // 从第 0 个 Byte 位置开始, 放置一个数字为 3 的 Short 类型数据(占 2 Byte)
- dataView.setInt32(2, 15); // 从第 2 个 Byte 位置开始, 放置一个数字为 15 的 Short 类型数据(占 4 Byte)
- dataView.setInt32(6, long.high); // 采用大端字节序放置
- dataView.setInt32(10, long.low);
- // 数据读取过程
- let shortNumber = dataView.getInt16(0);
- let intNumber = dataView.getInt32(2);
- let longNumber = Long.fromBits(dataView.getInt32(10), dataView.getInt32(6)); // 根据大端字节序读取, 该构造函数入参依次为: 低 16 位, 高 16 位
通过上面的示例, 我们将一串二进制数据转换成为了 JavaScript 中通用的数据类型
总结
通过使用 ArrayBuffer 和 DataView, 我们能够快速的将数字数据从二进制转换为 JavaScript 常用数据类型如 IntShort 等; 同时, 我们也可以将这些数据类型转换为二进制数据有了这些基础知识, 我们就能够理解在之后的博客中讲到的关于使用 WebSocket 进行二进制数据传递的过程和处理逻辑
来源: https://juejin.im/post/5abb560a6fb9a028d141262b