在JavaScript中什么地方使用ArrayBuffer和类型化数组?

e3bfsja2  于 2022-11-20  发布在  Java
关注(0)|答案(2)|浏览(154)

我正在从Node.js迁移到浏览器环境,但我仍然对ArrayBuffer和类型化数组(如Uint 8Array)感到困惑。
我很困惑在哪里使用类型化数组,在哪里直接使用ArrayBuffer。将一个转换成另一个并不难,反之亦然,但是在什么时候使用哪一个呢?
例如,当我在代码中创建一个表示数据块的对象时,它应该是ArrayBuffer还是Uint 8Array?它依赖于什么?
或者:我应该从我的函数返回ArrayBuffer(例如,对于外部API),还是类型化数组?
请注意,我可以搜索如何准确地将元素等添加到这些类型化数组中;我所缺少的是一些简短的通用指南,在哪里使用什么。特别是当从节点的缓冲区移动。

qhhrdooz

qhhrdooz1#

概念

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

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

要读取ArrayBuffer的内容,您需要使用 view。它位于顶部,并提供了一个“api”来访问不同宽度类型的字节,或任意访问。

宽度相关视图

根据您的需要使用不同的视图。如果您只需要读取字节值,即-128到127之间的有符号值或0到255之间的无符号值,您可以使用Int 8Array或Uint 8Array。请注意,它们的名称有点“误导”,因为它们是视图而不是数组,并且只引用底层的ArrayBuffer。
同样地,您也有Int8ArrayUint8ArrayUint8ClampedArrayInt16ArrayUint16ArrayInt32ArrayUint3ArrayFloat32ArrayFloat64Array的检视。
除了 * int 8Arrays之外,其他视图都对ArrayBuffer的大小有一定的要求。例如,Uint 32Array视图必须位于可被4整除的ArrayBuffer之上,否则会引发错误。int 16视图需要两个字节的边界。
这通常不是问题,因为您可以直接使用视图的构造函数指定索引的数量,并且将自动为它创建一个匹配的ArrayBuffer来满足这些要求。
由于ArrayBuffer是一个字节数组,因此 * int 16视图从中读取两个字节-或者,一个索引=两个字节,
int 32为四个,或者一个索引=四个字节,依此类推。
Uint 8Array和Uint 8 ClampedArray之间的主要区别是,超出范围的值将对普通数组进行模运算(例如256变为0)。在clamped数组中,值将按照建议进行箝位(256变为255)。

  • Int 16/Uint 16视图-每个索引代表两个字节,并且与内存对齐。*

  • Int 32/Uint 32和Float 32视图-每个索引代表四个字节,并与内存对齐。*

  • Float 64视图-每个索引代表8个字节,并与内存对齐。*

DataView提供灵活性

然后是DataView。它适用于需要灵活的ArrayBuffer并且需要从缓冲区中的位置读取可变宽度的情况,这些位置不一定是宽度或内存对齐的。
例如,* int 32索引将始终指向可被4整除的内存位置。另一方面,DataView可以从位置5读取Uint 32,并将在内部处理所有需要的步骤(位移位、掩码等),但代价是开销很小。
另一个不同之处是,DataView不使用索引,而是使用它所表示的数据的绝对字节位置,并且它自带了从任何位置读取或向任何位置写入各种宽度的方法。
x1c4d 1x指令集

  • DataView -可以从任何位置和任何宽度读取。*

在其他情况下,可以使用引用同一基础ArrayBuffer的多个不同视图。
目前没有整数的64位视图,但似乎是proposed for ES8

共享数组缓冲区

提到可跨Web工作者使用的新SharedArrayBuffers也很有用。
在过去的一些浏览器中,你可以(现在仍然可以)使用transferable objects,但是SharedArrayBuffers在内存保持不变的意义上更有效,只传输关于它的信息。SharedArrayBuffers不能像ArrayBuffers那样分离。

用途和使用领域

类型化数组很适合存储特定的数值并且速度很快。位图是类型化数组的典型候选对象(例如canvas 2D/WebGL)。
Web worker内部的大量数据处理是另一个用途,我已经提到了客户端和服务器或文件系统之间的二进制传输。
DataView非常适合解析或构建二进制文件和文件格式。
类型化数组是打包二进制数据以便通过网络发送到服务器或通过Web套接字和WebRTC的数据通道之类的东西的一种很好的方式。
如果处理音频、视频、画布或媒体录制,通常无法使用类型化数组。
使用类型化数组的关键是性能和内存。它们最常用于特殊情况,但在普通情况下,当你只需要存储数值(或utf-8字符串,加密向量等)时,使用它们也没有什么错。它们速度快,内存占用少。

注意事项

有几个注意事项需要注意:

字节顺序

必须对字节顺序采取一些预防措施。类型化数组始终反映它们运行时所处的CPU体系结构,即little-endian或big-endian。大多数使用者系统都是little-endian,但在使用 * int 16和 * int 32数组时,必须特别注意字节顺序。DataView也可以帮助解决这一问题,但如果性能很重要,则DataView并不总是一个好选择。
从服务器接收数据时,字节顺序也很重要。它们通常都是big-endian格式(AKA“网络顺序”)。解析文件格式时也是如此。

浮点数编码

Float 32/Float 64将读取和写入以IEEE-754编码的数字。如果几个视图用于同一个缓冲区,也需要注意这一点。

跨浏览器支持

现在大多数浏览器都支持类型化数组。如果你必须处理旧的浏览器,你必须回到IE9或旧的移动的浏览器才能不能使用它们。
Safari在性能方面并没有特别优化,但其他好处还是有的。5. 1版本不支持Float 64。
移动的设备有其自身的硬件限制,但一般而言:类型化数组是可以安全使用的。对于特殊情况,存在polyfill

yv5phkfx

yv5phkfx2#

我更喜欢在函数参数和返回类型中使用TypedArray。TypedArray可以表示ArrayBuffer的一个部分视图,这意味着它可能与底层缓冲区的大小不同。例如:

const buff = new ArrayBuffer(12);

// it will have the bytes from offset 4 to 8 (included)
const arr = new Uint8Array(buff, 4, 4);

数据的部分视图是函数的关注点,而不是整个底层缓冲区。
如果在这种情况下选择将ArrayBuffer传递到函数中会怎么样呢?然后必须创建一个新的ArrayBuffer,这是复杂的并且对性能有害的:

const buff = new ArrayBuffer(12);
foo(new Uint8Array(buff).slice(4, 9).buffer)

function foo(buff: ArrayBuffer) {
}

但是,如果您使用TypedArray,当您想将字节写入存储(如indexedDB)时要小心。确保TypedArray与底层缓冲区对齐,否则您可能会将整个数据写入存储。
如果愿意,您也可以选择同时接受ArrayBuffer和TypedArray:

function foo(data: ArrayBufferView | ArrayBuffer) {
  // Convert to a view, or any TypedArray you want
  if(!ArrayBuffer.isView(data)) data = new Uint8Array(data); 
  // ...
}

相关问题