我们知道在C语言中,可以使用malloc
和free
方法来分配和释放内存。随着web的发展中,js在ES6中新增了内存操作的支持。其实现方式就是---typed array。
typed array是个集体的概念。int8Array
,Uint8Array
,int16Array
,Uint16Array
等统统等称为typed array。通过这些类,开发者可以方便地读写内存中的二进制数据。
typed array在内部设计时分成了两部分:buffer
和view
。buffer
层表示内存中的数据块,view
负责提供操作数据块的接口。
ArrayBuffer
buffer
层的底层实现就是基于ArrayBuffer
类。ArrayBuffer
的功能与malloc
类似,为用户分配一块内存。
创建一个ArrayBuffer
实例很简单,它接收一个参数,参数表示要分配多大的一块内存区域,字节为单位。下面的代码片段,分配了一块8字节的内存区:
var buffer = new ArrayBuffer(8);
我们还可以通过byteLength
访问该实例的内存大小:
console.log(buffer.byteLength) // 8
使用slice
方法创建一个新的实例,其内容复制原ArrayBuffer
实例中的部分内容。
var buf2 = buffer.slice(0,2);
创建一个新的实例,分配2字节大小的内存,其内容复制buffer
中索引为0和1的内存中的数据。
但是ArrayBuffer
并不提供对内存读写的方法。如果要对该内存块进行操作,需要用到另一个类DataView
。
DataView
DataView
类提供了访问ArrayBuffer
的接口。其语法如下:
new DataView(buffer [, byteOffset [, byteLength]])
首先接收一个必传的buffer
对象,该对象就是DataView
实例接下来将要操作的内存块。其次是一个可选的byteOffset
,该参数表示DataView
实例要操作的buffer
对象的开始位置。最后的参数指定要操作的buffer
对象中元素的个数。
下面的代码片段演示了DataView
如何操作ArrayBuffer
:
var buffer = new ArrayBuffer(16);
var dv = new DataView(buffer, 0);
dv.setInt16(1, 42);
dv.getInt16(1); //42
示例中创建了一个DataView
实例,该实例将buffer
作为被操作的内存块,并且指定该内存块的16个字节都位于可操作区。
接着调用setInt16
方法向从buffer
的第2个字节开始存储带符号的16位整数。调用getInt16
方法读出位于第2个字节处的带符号的16位整数。
typed array
如上所述,typed array的实现是组合了ArrayBuffer
和DataView
。也就是说typed array 通过ArrayBuffer
创建内存块,通过DataView
实现对内存块的读写操作。
typed array提供了多个类实现对同一块内存中的二进制数据按照不同的位数格式进行读写。如使用int8Array
从内存中以带符号的8位整数的形式读写数据。
下面看一个例子来加深对typed array的理解
var buffer = new ArrayBuffer(16) //创建16b的内存区
var int32View = new Int32Array(buffer);
var int16View = new Int16Array(buffer);
for (var i = 0; i < int32View.length; i++) {
int32View[i] = i * 2;
console.log(int32View[i]); // 0, 2, 4, 6
}
for (var i = 0; i < int16View.length; i++) {
console.log(int16View[i]); // 0, 0, 2, 0, 4, 0, 6, 0
}
buffer
是一个ArrayBuffer
实例,表示一块16b的内存区。同时创建了两个typed array,分别是32位和16位的。区别在于使用32位的访问buffer
时,会把buffer
(16b)以32位为单位划分为4个元素,读写都是以32位为单位进行的。16位的进行访问时,会把buffer
(16b)以16位为单位划分为8个元素,每次读写都是16位。
第一个循环中,我们使用int32View
向buffer
中的4个元素中分别写入了0,2,4,6
4个整数。
很显然console
中的结果是0,2,4,6
。
第二个循环中,我们使用int16View
以16位的格式读取同一块内存,可以想象原来的4个元素变成了8个元素,因此读出的时候也会将原来的32位元素拆分为2个16位元素。因此console
中的结果是0,0,2,0,4,0,6,0
。
有同学可能对输出的顺序不理解,觉得为什么不是0,0,0,2,0,4,0,6
呢。这里稍微解释一下。
原因是默认情况下typed array 读取数据的时候都是以小端模式来返回的。
所谓的小端模式(Little-endian),是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
上例在小端模式下的存储形式,每个框框表示16位。小端模式下数据的位权和地址的顺序是一致的,从小到大排列。
总结
typed array提供了一组读取不同字长的类数组对象如,int8Array
,int16Array
,int64Array
来实现对内存的操作。typed array的底层实现是基于ArrayBuffer
和DataView
实现的,对内存的分配和内存的读取做了很好的分层设计。使用typed array使js向C语言等具备了操作二进制数据的能力。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。