在JavaScript中使用ArrayBuffer和类型数组的位置?

Kar*_*lek 30 javascript arraybuffer

我正在从Node.js迁移到浏览器环境,我仍然对ArrayBuffer与类型化数组(例如Uint8Array)感到困惑.

我很困惑在哪里使用类型化数组,以及在哪里直接使用ArrayBuffer.将一个转换为另一个并不难,反之亦然,但何时使用?

例如,当我创建一个代表我代码中的一大块数据的对象时,它应该是ArrayBuffer还是Uint8Array?它取决于什么?

或者:我应该ArrayBuffer从函数(例如,外部API)或类型化数组返回吗?

请注意,我可以谷歌如何向这些类型化数组添加元素等; 我缺少的是一些简短的一般指南,在哪里使用.特别是从节点缓冲区移动时.

小智 61

概念

ArrayBuffer表示物理内存中的字节数组.ArrayBuffer是字节的实际存储空间,但很少直接使用 - 实际上,您无权直接读取ArrayBuffer的内容,只能为其传递引用.另一方面,它们用于服务器和客户端之间的二进制数据传输,或者用于通过Blob从用户的文件系统传输.

内存中的ArrayBuffer字节数组
内存中的ArrayBuffer字节数组 - 每个索引等于一个字节.ArrayBuffer在内存中对齐.

要读取ArrayBuffer的内容,您需要使用视图.它位于顶部并提供"api"以通过不同宽度类型或任意方式访问字节.

宽度相关的视图

根据您的需要使用不同的视图.如果你只需要读取字节值,即.在-128和127之间的有符号值 - 或 - 0-255之间的无符号值,您将使用Int8Array或Uint8Array.请注意,它们的名称有点"误导",因为它们是视图而不是数组,并且只引用底层的ArrayBuffer.

同样,您有Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint3Array,Float32ArrayFloat64Array的视图.

除了*int8Arrays之外,其他人对ArrayBuffer大小有一些要求.例如,Uint32Array视图必须位于可被4整除的ArrayBuffer之上,否则会引发错误.*int 16个视图需要一个双字节边界.

这通常不是问题,因为您可以直接使用视图的构造函数指定索引数,并且将自动创建匹配的ArrayBuffer以满足这些要求.

由于ArrayBuffer是一个字节数组,因此*int16视图从中读取两个字节 - 或者,一个索引=两个字节,*int32四个,或一个索引=四个字节,依此类推.

Uint8Array和Uint8ClampedArray之间的主要区别在于,范围之外的值受普通数组的模数(例如256变为0).在钳位阵列中,值被建议钳位(256变为255).

*int16视图
Int16/Uint16视图 - 每个索引代表两个字节并且是内存对齐的.

*int32视图
Int32/Uint32和Float32视图 - 每个索引代表四个字节并且是内存对齐的.

Float64视图
Float64视图 - 每个索引代表八个字节并且是内存对齐的.

DataView的灵活性

然后是DataView.这适用于需要灵活的ArrayBuffer并且需要读取可变宽度和缓冲区中不一定是宽度或内存对齐的位置的情况.

例如,*int32索引将始终指向可分为四的内存位置.另一方面,DataView可以从位置5读取Uint32,并将在内部处理所有需要的步骤(位移,屏蔽等),但代价是微小的开销.

另一个区别是DataView不使用索引而是使用它所代表的数据的绝对字节位置,并且它有自己的方法来从/向任何位置读取或写入各种宽度.

数据视图
DataView - 可以从任何位置和任何宽度读取.

在其他情况下,您可以使用引用相同底层ArrayBuffer的多个不同视图.

当前整数不存在64位视图,但似乎是针对ES8提出的.

SharedArrayBuffers

提及可以跨Web worker使用的新SharedArrayBuffers也很有用.

您可以(并且仍然可以)在某些浏览器中使用可转移对象,但SharedArrayBuffers在内存保持不变的意义上更有效,只传输有关它的信息.SharedArrayBuffers不能像ArrayBuffers那样分离.

目的和用途领域

类型化数组很适合存储特定的数值并且速度很快.位图是类型化数组的典型候选者(例如canvas 2D/WebGL).

Web工作者内部数据的大量数据处理是另一种用途,等等.我已经提到了客户端和服务器或文件系统之间的二进制传输.

DataViews非常适合解析或构建二进制文件和文件格式.

类型化数组是打包二进制数据以便通过网络发送到服务器或通过Web套接字以及WebRTC数据通道等方式的绝佳方式.

如果您处理音频,视频,画布或媒体录制,通常无法使用类型化数组.

使用类型化数组的关键是性能和内存.它们最常用于特殊场景,但在普通情况下只需存储数值(或utf-8字符串,加密向量等)时使用它们并没有错.它们速度快,内存占用少.

注意事项

需要注意以下几项预防措施:

字节顺序

必须对字节顺序采取一些预防措施.类型化数组总是反映它们运行的​​CPU架构,即.little-endian或big-endian.大多数消费者系统都是little-endian,但在使用*int16和*int32数组时,必须特别注意字节顺序.DataView也可以帮助这个部分,但如果性能很重要,并不总是一个好的选择.

从服务器接收数据时,字节顺序也很重要.它们通常始终采用大端格式(AKA"网络顺序").对于解析文件格式,将适用相同的格式.

浮点数编码

Float32/Float64将读取和写入以IEEE-754格式编码的数字.如果多个视图用于同一缓冲区,也需要注意这一点.

跨浏览器支持

现在大多数浏览器都支持类型化数组.如果您必须处理旧版浏览器,则必须返回IE9或更早版本的移动浏览器才能使用它们.

Safari在性能方面没有特别优化,但其他好处还有.版本5.1不支持Float64.

移动设备有自己的硬件限制,但一般来说:类型化阵列可以安全使用.对于特殊情况,存在polyfill.

  • `const memory = new ArrayBuffer(2); const view = new Uint8Array(memory); view [0] = 0xFF; console.log(new Uint8Array(memory)); // Uint8Array(2)[255,0]`.所以基本上`ArrayBuffer`是实际内存,`DataView`是以各种大小为单位访问内存的包装器,_Typed array_与`DataView`相同,只是它以固定大小的单位访问.谢谢. (7认同)
  • 周围是否有人仍然对此一无所知并寻找其他资源......(像我一样) (5认同)
  • 感谢您的惊人回答。所以,没有理由正确使用 ArrayBuffer,对吧。伟大的。 (2认同)
  • 这根本没有回答OP的问题,即是否应该传递并使用“ArrayBuffer”或“Uint8Array”之类的视图;如果使用其中一种而不是另一种有警告...... (2认同)