JavaScript读写二进制数据的方法详解


Posted in Javascript onSeptember 09, 2018

前言

二进制是计算技术中广泛采用的一种数制。二进制数据是用0和1两个数码来表示的数,如果想要在前端中处理音频和视频。那你必须要对二进制数据有很好地掌握和操作能力。下面话不多说了,来一起看看详细介绍的吧

类型化数组的出现

类型化数组是 HTML5 中引入的API,它能够让开发者使用 JavaScript 直接操作二进制数据。在类型化数组出现之前,我们是无法直接通过 JavaScript 操作二进制数据,通常都是操作 JavaScript 中的数据类型,由运行时转化成二进制。这就多了一个转化的过程,尽管 JavaScript 对数据类型做了很多优化以提高效率,但相比直接操作二进制来说,仍然有效率上的差异。于是类型化数组就顺势推出了。

用途

那么,类型化数组的应用场景都有哪些呢?

  • canvas 图像处理。
  • WebGL 与显卡通信。
  • 文件操作
  • Ajax响应

如何使用

那么,既然类型化数组这么重要,那还等什么,赶紧来掌握它们吧。

既然我们要直接操作二进制数据,二进制数据又是存放在一段连续的内存区域中,所以我们首先要有这么一段内存区域。

我们可以创建一个内存区域:

let buffer = new ArrayBuffer()

ArrayBuffer 是一个构造函数,允许我们实例化数组缓冲区,数组缓冲区可以理解为是一段连续的内存区域。

由于我们构造函数传入的参数是空,所以生成的 buffer 指向的内存长度是 0 字节,没有意义。

嗯,那我们就创建一个有意义的内存区域。

buffer = new ArrayBuffer(8)

我们给ArrayBuffer 传入参数 8,意思是让浏览器帮我们创建一段 8 个字节长度的内存区域。

我们看下这段内存区域的长度是否是 8 个字节

console.log(buffer.byteLength);

输出是 8, 看来浏览器没有欺骗我们。

JavaScript读写二进制数据的方法详解

我们猜想这 8 个字节里面的值应该都是 0 ,因为我们并没有给 buffer 赋值。让我们确认一下吧,先看第一个字节:

console.log(buffer[0])

输出: undefined。

咦?怎么是 undefined 呢?

哦,原来 buffer[0] 的意思是查看 buffer 这个对象 的属性为 0 的值,因为 buffer 没有 0 这个属性,所以是 undefined。

好吧,看来我们查看 buffer 内容的姿势不对。

那该如何查看 buffer 内容呢?

数组视图

????,八大金刚闪亮登场~

Int8Array:8 位有符号整数,长度 1 个字节。
Uint8Array: 8位无符号整数, 1 个字节长度。
Int16Array:16位有符号整数, 2 个字节长度。
Uint16Array:16位无符号整数,2 个字节长度。
Int32Array:32位有符号整数, 4 个字节长度。
Uint32Array:32位无符号整数, 4 个字节长度。
Float32Array:32位浮点数, 4 个字节长度。
Float64Array:64位浮点数,8 个字节长度。

这八大金刚有什么神通呢?我们无法直接读写 buffer 数据,而这八种数据类型充当了读写 buffer 内容的桥梁。

JavaScript读写二进制数据的方法详解

还是上面的 buffer,我们想查看一下 buffer 内容。首先创建一个读写该 buffer 的桥梁:

let int8Array = new Int8Array(buffer);

我们创建了一个读写 buffer 的桥梁,用 8 位有符号整数来读写 buffer。那现在我们看看 buffer 第一位的内容是什么吧?

console.log(int8Array[0]);

输出:0,看来初始化的时候,buffer 的各个字节存储的值默认都是 0 了。

JavaScript读写二进制数据的方法详解

我们看看如何使用 Int8Array 给 buffer 赋值:

int8Array[0] = 30;
int8Array[1] = 41;

int8Array[2] = 52;
int8Array[3] = 63;

int8Array[4] = 74;
int8Array[5] = 85;

int8Array[6] = 86;
int8Array[7] = 97;

很简单,因为 Int8Array 是一个字节的长度,和 buffer 的单位一致,所以我们可以通过索引的形式对 buffer 指定位置进行赋值操作。

JavaScript读写二进制数据的方法详解

很简单吧?就是这么简单。

另外七大金刚和 Int8Array 的用法一样,但是有所不同,我们看下他们之间的区别。

这次我们使用 Int16Array,仍然是刚才的 buffer,我们创建一个新的桥梁,这座桥梁仍然通往 buffer 。

let int16Array = new Int16Array(buffer);

大家试想一下 int16Array[0] 是什么内容?

我们输出一下:

console.log(int16Array[0])

咦,结果怎么是 10526?

不太理解了。好吧,我们分析下 10526 怎么得来的。

我们看下 buffer 中的二进制数据。

由于 Int16Array 占两个字节,所以我们在用它读写数据的时候,一个索引所代表的数据等于 buffer 中两个字节。

JavaScript读写二进制数据的方法详解

我们可以看到 int16Array[0] 里面的二进制数据是由30的二进制和41的二进制数据拼接而成:00011110(30)  00101001(41)。

我们按照 30、41的顺序计算一下二进制对应的十进制数。

parseInt(1111000101001, 2) //输出 7721

算出来的值是 7721,这和我们输出的不一致呀?

这就涉及到字节顺序的概念。在我们的个人笔记本上一般都是小端字节序。小端字节序体现在我们这个示例中即是 41、30的二进制顺序,我们刚才的计算顺序有问题,那按照 41、30 的二进制顺序计算一下

parseInt(10100100011110, 2) //输出 10526

可以看到输出结果是 10526,和我们直接使用 int16Array[0]

得出的结果一致。

上面这个例子,告诉我们在换数据结构解析 buffer 的时候,数据会变得不容易理解,我们一定要谨慎处理。

属性和方法

类型化数组实例化的对象包含一些很有用的属性和方法:

length

length属性返回类型化数组的数据成员个数。

byteLength

返回类型化数组的字节长度。注意与length的区别。通长 byteLength = length * 每个数据占用字节数

byteOffset

返回该类型化数组的数据从所处 buffer 中的哪个字节开始。

buffer

类型化数组对应的 buffer。

set

复制数组,将某段内存中的数据完整地复制到另一段内存。

let a = new Uint8Array(12);
a[0] = 31;
a[1] = 32;
let b = new Uint8Array(12);
b.set(a);

上面这段代码的意思是将 a 这段buffer中的内容,完整地拷贝到 b 这段 buffer 中,这种方式比按索引赋值要快速地多。
当然,set 支持从某个索引开始复制数据

let a = new Uint8Array(12);
a[0] = 31;
a[1] = 32;
let b = new Uint8Array(10);
b.set(a, 2);

上面这段代码意思是从b的第三个索引位置开始复制 a 中的数据。

subarray

subarray的意思是对一个类型化数组,取其子数组的内容,返回一个新的类型化数组。

let a = new Uint8Array(8);
a[2] = 1;
let b = a.subarray(2,3);
console.log(b.length);
console.log(b.byteLength);

subarray 的第一个参数,代表从源数组的第几个索引开始截取,第二个参数代表截取到第几个索引。

混合视图

有一点需要注意,我们的类型数组初始化的时候,可以指定 buffer的某一段,这就意味着,我们可以对一段 buffer 内存区域指定多个类型数,我们称之为 混合视图。

let buffer = new Buffer(8);
let idArray = new Int8Array(buffer, 0,2);
let nameArray = new Int8Array(buffer, 2, 4);
let ageArray = new Int8Array(buffer, 6, 2);

我们用一段内存区域表示一个人的 id、name、age。这种结构类似于 C 语言中的 struct 。

JavaScript读写二进制数据的方法详解

我们将一段 8 个字节的内存分成三个部分:

  • 字节 0 ~ 字节 1 代表 id。
  • 字节 2 ~ 字节 5 代表 username。
  • 字节 6 ~ 字节 7 代表 age。

DataView

JavaScript 还引入了另外种视图DataView,也能达到操作 buffer 的目的,但相比之下,DataView 操作粒度更细一些,而且还能够设置字节序为大端还是小端。

DataView 的构造函数:

DataView(ArrayBuffer对象 buffer, 从 buffer 的第几个字节开始读取, 读取的长度);

举个例子来说:

let buffer = new ArrayBuffer(10);
let view = new DataView(buffer);

如何读取?

我们创建好了视图 view, 那该如何读取呢?

  • getInt8(index, order):从第 index 个字节读取一个 8 位整数。
  • getUint8(index, order):从第 index 个字节开始读取一个无符号的 8 位整数。
  • getInt16(index, order):从第 index 个字节开始读取 2 个字节,返回一个 16 位整数。
  • getUint16(index, order):从第 index 个字节开始读取 2 个字节,返回一个无符号的 16 位整数。
  • getInt32(index, order):从第 index 个字节开始读取 4 个字节,返回一个32位的整数。
  • getUint32(index, order):从第 index 个字节开始读取 4 个字节,返回一个无符号的 32 位整数。
  • getFloat32(index, order):从第 index 个字节开始读取 4 个字节,返回一个 32 位 浮点数。
  • getFloat64(index, order):从第 index 个字节开始读取 8 个字节,返回一个 64 位的浮点数。

JavaScript 提供了 8 种读取方式,功能很简单,也很容易理解,这里就不一一做示例了,大家可以自己试一下。

刚刚我们也说了,DataView 也支持设置字节序,在上面 8 中读取方式中,第一个字节是索引,第二个字节允许我们设置字节序,true 代表 小端字节序读取,false 代表大端字节序读取,默认为 false。

如何写入?

DataView 不仅能支持细粒度的读取操作,也支持细粒度的写入操作:

  • setInt8(index, value, order):从第 index 个字节开始,写入 1 个字节的值为 value 的 8 位整数。
  • setUint8(index, value, order):从第 index 个字节开始,写入 1 个字节的值为 value 的无符号 8 位整数。
  • setInt16(index, value, order):从第 index 个字节开始,写入 2 个字节的值为 value 的 16 位整数。
  • setUint16(index, value, order):从第 index 个字节开始,写入 2 个字节的值为 value 的无符号的 16 位整数。
  • setInt32(index, value, order):从第 index 个字节开始,写入 4 个字节的值为 value 的 32 位整数。
  • setUint32(index, value, order):从第 index 个字节开始,写入 4 个字节的值为 value 的无符号 32 位整数。
  • setFloat32(index, value, order):从第 index 个字节开始,写入 4  个字节的值为 value 的 32 位浮点数。
  • setFloat64(index, value, order):从第 index 个字节开始,写入 8  个字节的值为 value 的 64 位浮点数。

order 的意思仍然是设置写入时的字节序, true 为小端字节序,false 为大端字节序,默认为 false。
用法也很简单,大家可以练习一下。

至此,关于 JavaScript 操作二进制的方式就介绍完了,大家以后碰到需要直接操作内存的场景时,不妨用用这两种方式。

未来的你会感谢今天努力的你。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
Javascript中找到子元素在父元素内相对位置的代码
Jul 21 Javascript
javascript 判断中文字符长度的函数代码
Aug 27 Javascript
jQuery实现的多选框多级联动插件
May 02 Javascript
使用jquery动态加载js文件的方法
Dec 24 Javascript
JavaScript必知必会(六) delete in instanceof
Jun 08 Javascript
微信小程序开发实战教程之手势解锁
Nov 18 Javascript
jQuery实现标签子元素的添加和赋值方法
Feb 24 jQuery
Js中将Long转换成日期格式的实现方法
Jun 05 Javascript
js、jquery实现列表模糊搜索功能过程解析
Mar 27 jQuery
vue 解决在微信内置浏览器中调用支付宝支付的情况
Nov 09 Javascript
ES6学习教程之Promise用法详解
Nov 22 Javascript
前端JS获取URL参数的4种方法总结
Apr 05 Javascript
详解在vue-cli中使用graphql即vue-apollo的用法
Sep 08 #Javascript
在vue项目中集成graphql(vue-ApolloClient)
Sep 08 #Javascript
React Native开发封装Toast与加载Loading组件示例
Sep 08 #Javascript
原生JS实现前端本地文件上传
Sep 08 #Javascript
JS实现可视化文件上传
Sep 08 #Javascript
js实现文件上传功能 后台使用MultipartFile
Sep 08 #Javascript
vue-router的HTML5 History 模式设置
Sep 08 #Javascript
You might like
PHP simple_html_dom.php+正则 采集文章代码
2009/12/24 PHP
PHP 5.3.1 安装包 VC9 VC6不同版本的区别是什么
2010/07/04 PHP
用来解析.htgroup文件的PHP类
2012/09/05 PHP
php实现递归与无限分类的方法
2015/02/16 PHP
php递归函数三种实现方法及如何实现数字累加
2015/08/07 PHP
PHP中串行化用法示例
2016/11/16 PHP
jQuery实现的鼠标滑过弹出放大图片特效
2016/01/08 Javascript
探索angularjs+requirejs全面实现按需加载的套路
2016/02/26 Javascript
JavaScript数据存储 Cookie篇
2016/07/02 Javascript
php 修改密码实现代码
2017/05/24 Javascript
vue Element-ui input 远程搜索与修改建议显示模版的示例代码
2017/10/19 Javascript
vue弹窗消息组件的使用方法
2020/09/24 Javascript
vue 实现边输入边搜索功能的实例讲解
2018/09/16 Javascript
js获取form表单中name属性的值
2019/02/27 Javascript
JS自定义对象创建与简单使用方法示例
2020/01/15 Javascript
原生javascript制作的拼图游戏实现方法详解
2020/02/23 Javascript
浅谈vue-props的default写不写有什么区别
2020/08/09 Javascript
Python数据类型中的“冒号“[::]——分片与步长操作示例
2018/01/24 Python
通过python将大量文件按修改时间分类的方法
2018/10/17 Python
Python中的 sort 和 sorted的用法与区别
2019/08/10 Python
Python单元测试工具doctest和unittest使用解析
2019/09/02 Python
python实现简单的购物程序代码实例
2020/03/03 Python
python 已知三条边求三角形的角度案例
2020/04/12 Python
什么是Python变量作用域
2020/06/03 Python
keras实现VGG16方式(预测一张图片)
2020/07/07 Python
Python爬虫破解登陆哔哩哔哩的方法
2020/11/17 Python
CSS3实现类似翻书效果的过渡动画的示例代码
2019/09/06 HTML / CSS
css3实现画半圆弧线的示例代码
2017/11/06 HTML / CSS
StubHub西班牙:购买和出售全球活动门票
2017/06/05 全球购物
绘儿乐产品官方在线商店:Crayola.com
2019/09/07 全球购物
摄影助理岗位职责
2014/02/07 职场文书
大四学生找工作的自荐信
2014/03/27 职场文书
干部四风问题整改措施思想汇报
2014/10/13 职场文书
考试作弊检讨书1000字(5篇)
2014/10/19 职场文书
关于JS中的作用域中的问题思考分享
2022/04/06 Javascript
win11无线投屏在哪设置? win11无线投屏功能的使用方法
2022/04/08 数码科技