当前位置: 首页 > Web前端 > JavaScript

ArrayBuffer二进制数据

时间:2023-03-26 22:26:17 JavaScript

在web开发中,我们在处理文件(创建、上传、下载)时,经常会遇到二进制数据。另一个典型的应用场景是图像处理。与其他语言相比,JavaScript中的二进制数据以非标准方式实现。1.创建二进制数据基本的二进制对象是一个ArrayBuffer——对固定长度的连续内存空间的引用。让缓冲区=新的ArrayBuffer(16);//创建一个长度为16的bufferalert(buffer.byteLength);//16,它将分配一个16字节的连续内存空间并预先填充0注意:ArrayBuffer不是某种ArraysofStuff让我们首先澄清一个可能的误解。ArrayBuffer与Array没有任何共同之处:它具有固定长度,我们不能增加或减少它的长度。它在内存中占用了那么多空间。要访问单个字节,需要另一个“视图”对象,而不是buffer[index]。ArrayBuffer是一个内存区域。它存储了什么?没办法判断。只是一个原始的字节序列。2.操作二进制数据要操作ArrayBuffer,我们需要用到“视图”对象。2.1视图对象视图对象本身不存储任何东西。它是一副“眼镜”,通过它来解释存储在ArrayBuffer中的字节。示例:Uint8Array-将ArrayBuffer中的每个字节视为0到255之间的单个数字(每个字节为8位,因此只能容纳这么多)。这称为“8位无符号整数”。Uint16Array-将每2个字节视为0到65535之间的整数。这称为“16位无符号整数”。Uint32Array-将每4个字节视为0到4294967295之间的整数。这称为“32位无符号整数”。Float64Array-将每8个字节视为5.0x10-324和1.8x10308之间的浮点数。因此,16字节ArrayBuffer中的二进制数据可以解释为16个“小数字”,或8个较大数字(每个2个字节),或4个较大数字(每个4个字节)。字节),或2个高精度浮点数(每个8字节)。2.2使用View操作二进制数据ArrayBuffer是核心对象,是一切的基础,是原始的二进制数据。但是,如果我们想写入一个值或对其进行迭代,基本上几乎所有操作-我们都必须使用视图,例如:letbuffer=newArrayBuffer(16);//创建一个长度为16的bufferletview=newUint32Array(buffer);//将缓冲区视为32位整数序列alert(Uint32Array.BYTES_PER_ELEMENT);//每个整数4个字节alert(view.length);//4,它存储4个整数alert(view.byteLength);//16,字节大小//让我们写一个值view[0]=123456;//遍历值for(letnumofview){alert(num);//123456,然后是0、0、0(共4个值)}2.3TypedArray所有这些视图(Uint8Array、Uint32Array等)的总称是TypedArray。它们共享同一组方法和属性。请注意,没有名为TypedArray的构造函数,它只是ArrayBuffer上的一个视图的总称。Int8Array、Uint8Array等,完整列表即将推出。当您看到类似newTypedArray的内容时,它表示newInt8Array、newUint8Array等之一。类型化数组的行为类似于常规数组:它们是索引和可迭代的。类型化数组的构造函数(无论是Int8Array还是Float64Array,都无关紧要)的行为不同并且取决于参数类型。参数有5种变体:newTypedArray(buffer,[byteOffset],[length]);newTypedArray(object);newTypedArray(typedArray);newTypedArray(length);newTypedArray();`如果给定的是ArrayBuffer参数,将在其上创建视图。我们已经使用过这种语法。可选地,我们可以给一个起始位置byteOffset(默认为0)和长度(默认到缓冲区的末尾),这样视图将只覆盖缓冲区的一部分。如果给定一个数组或任何类似数组的对象,则会创建一个相同长度的类型化数组并复制其内容。我们可以用它来预填充数组数据:letarr=newUint8Array([0,1,2,3]);警报(arr.length);//4、创建一个等长的二进制数组alert(arr[1]);//1,用给定值(无符号8位整数)填充4个字节`如果给定另一个TypedArray,则相同:创建一个相同长度的TypedArray,并复制其内容。如有必要,数据会在此过程中转换为新类型。让arr16=newUint16Array([1,1000]);让arr8=newUint8Array(arr16);警报(arr8[0]);//1个警报(arr8[1]);//232,试图复制1000,但无法将1000放入一个八位字节(详情见下文)。对于数字参数长度——创建一个类型化数组以包??含这么多元素。它的字节长度将是长度乘以单个TypedArray.BYTES_PER_ELEMENT中的字节数:`letarr=newUint16Array(4);//为4个整数创建类型化数组alert(Uint16Array.BYTES_PER_ELEMENT);//每个整数2字节alert(arr.byteLength);//8(字节大小)`不带参数,创建一个长度为零的类型化数组。我们可以直接创建一个TypedArray,不用提ArrayBuffer。但是,视图离不开底层的ArrayBuffer,因此除了第一种情况(提供ArrayBuffer的情况)之外,在所有情况下都会自动创建ArrayBuffer。如果要访问底层的ArrayBuffer,那么TypedArray中有如下属性:arr.buffer-引用ArrayBuffer。arr.byteLength-ArrayBuffer的长度。所以我们总是可以从一个视图转到另一个视图:letarr8=newUint8Array([0,1,2,3]);//相同数据的另一个视图以下是类型化数组的列表:Uint8Array、Uint16Array、Uint32Array-用于8、16和32位整数。Uint8ClampedArray-用于8位整数,其值在分配时被“钳制”(见下文)。Int8Array、Int16Array、Int32Array-用于有符号整数(可以为负数)。Float32Array、Float64Array–32位和64位带符号浮点数。没有int8或类似的单值类型请注意,尽管有像Int8Array这样的名称,但在JavaScript中没有像int或int8这样的单值类型。这是合乎逻辑的,因为Int8Array不是这些单个值的数组,而是ArrayBuffer上的视图。2.4越界行为如果我们尝试将越界值写入类型化数组,会发生什么情况?不会报错。但是多余的位被切断了。例如,我们尝试将256放入Uint8Array。二进制格式的256是100000000(9位),但是Uint8Array每个值只有8位,因此可用范围是0到255。对于更大的数字,只存储最右边(LSB)的8位,其余的被切掉off:所以结果为0。257的二进制格式为100000001(9位),存储的是最右边的8位,所以数组中会有1:也就是说这个数模28的结果是保存。例子如下letuint8array=newUint8Array(16);letnum=256;alert(num.toString(2));//100000000(二进制表示)uint8array[0]=256;uint8array[1]=257;alert(uint8array[0]);//0警报(uint8array[1]);//1Uint8ClampedArray在这方面很特殊,它的行为不同。对于大于255的任何数字,它将存储255,对于任何负数,它将存储0。此行为对图像处理很有用。3.TypedArray方法TypedArray有常规的Array方法,但有一个明显的例外。我们可以迭代、映射、切片、查找和减少等。但是有一些事情我们不能做:没有拼接——我们不能“删除”一个值,因为类型化数组是缓冲区的视图,缓冲区是固定的,连续的内存区域。我们所能做的就是分配一个零值。没有连接方法。还有其他两种方法:arr.set(fromArr,[offset])将fromArr中的所有元素复制到arr,从偏移量(默认为0)开始。arr.subarray([begin,end])从开始到结束(不包括)创建一个相同类型的新视图。这类似于slice方法(也支持),但不复制任何东西——只是创建一个新视图来操作给定切片的数据。使用这些方法,我们可以复制、混合类型数组、从现有数组创建新数组等。4.DataViewDataView是ArrayBuffer上一种特殊的超灵活“无类型”视图。它允许以任何格式访问任何偏移量的数据。对于类型化数组,构造函数决定其格式。整个数组应该是统一的。第i个数是arr[i]。使用DataView,我们可以使用.getUint8(i)或.getUint16(i)等方法访问数据。我们在方法调用时选择格式,而不是在构造时。语法:newDataView(buffer,[byteOffset],[byteLength])buffer-底层ArrayBuffer。与类型化数组不同,DataView不创建自己的缓冲区。我们需要提前准备。byteOffset–视图的起始字节位置(默认为0)。byteLength-视图的字节长度(默认为缓冲区末尾)。例如,这里我们从同一个缓冲区中提取不同格式的数字://4字节二进制数组,每个数组的最大值为255letbuffer=newUint8Array([255,255,255,255]).buffer;letdataView=newDataView(buffer);//在偏移量0处获取一个8位数字alert(dataView.getUint8(0));//255//现在在偏移量0处得到一个16位数字,它由2个字节组成,一起解析为65535alert(dataView.getUint16(0));//65535(最大的16位无符号整数)//在偏移量0处获取一个32位数字alert(dataView.getUint32(0));//4294967295(最大的32位无符号整数)dataView.setUint32(0,0);//将4个字节的数字设置为0,即所有字节都设置为0`当我们在同一个缓冲区中存储混合格式的数据时,DataView很有用。例如,当我们存储一对序列(16位整数,32位浮点数)时,可以使用DataView轻松访问它们。2.总结ArrayBuffer是核心对象,它是对一块固定长度的连续内存区域的引用。几乎所有对ArrayBuffer的操作都需要一个视图。它可以是TypedArray:Uint8Array、Uint16Array、Uint32Array-用于8位、16位和32位无符号整数。Int8Array、Int16Array、Int32Array-用于有符号整数(可以为负数)。Float32Array、Float64Array–32位和64位带符号浮点数。或DataView-使用方法指定视图的格式,例如getUint8(offset)。在大多数情况下,我们直接创建和操作类型化数组,将ArrayBuffer隐藏为“公分母”。我们可以通过.buffer访问它,并在需要时创建另一个视图。还有两个其他术语用于描述对二进制数据进行操作的方法:ArrayBufferView是所有这些视图的总称。BufferSource是ArrayBuffer或ArrayBufferView的统称。我们将在下一章学习这些术语。BufferSource是最常用的术语之一,因为它表示“任何类型的二进制数据”——一个ArrayBuffer或其上的视图。这是一个备忘单: