ArrayBuffer

基本的二进制对象是 ArrayBuffer —— 对固定长度的连续内存空间的引用

const buffer = new ArrayBuffer(16) // 创建一个长度为 16 Byte 的 buffer
console.log(buffer.byteLength) // 16

它会分配一个 16 字节的连续内存空间,并用 0 进行预填充

Warning ArrayBuffer 不是 Array

ArrayBuffer 是对固定长度的连续内存空间的引用,不同与 Array

  • 它的长度是固定的,无法增加或减少它的长度
  • 它正好占用了内存中的那么多空间
  • 要访问单个字节,需要另一个“视图”对象,而不是 buffer[index]
  • 没有 Array.prototypepushpop 等方法

类型化数组

ArrayBuffer 是一个内存区域,它里面存储了什么无从判断,只是一个原始的字节序列。想要操作 ArrayBuffer,需要使用“视图”对象。视图对象本身并不存储任何东西,它是一副“眼镜”,透过它来解释存储在 ArrayBuffer 中的字节

类型化数组每个元素所占字节数描述(C 语言中的数据类型)
Int8Array18 位整型数(signed char
UInt8Array18 位无符号整型数(unsigned char
Uint8ClampedArray18 位无符号整型数,溢出时有特殊处理
Int16Array216 位整型数(signed short
UInt16Array216 位无符号整型数(unsigned short
Int32Array432 位整型数(signed int
UInt32Array432 位无符号整型数(unsigned int
BigInt64Array864 位整型数(signed long / long long
BigUint64Array864 位整型数(unsigned long / long long
Float32Array4单精度 32 位浮点数(float
Float64Array8双精度 64 位浮点数(double

构造器

书写 TypedArray 的说明

没有名为 TypedArray 的构造器,它只是表示 ArrayBuffer 上的视图之一的通用总称术语

当你看到 new TypedArray 之类的内容时,它表示 new Int8Arraynew Uint8Array 及其他中之一

  • new TypedArray(buffer, byteOffset?, length?)
    • bufferArrayBufferSharedArrayBuffer
    • byteOffset:视图的起始字节位置(即:跳过几个字节长度的数据),默认为 0
    • length:视图的字节长度,默认至 buffer 的末尾
  • new TypedArray(object)
    • 给定的是 Array,或类数组对象,或可迭代对象,则会创建一个相同长度的类型化数组,并复制其内容
    • 如:new Uint8Array([0, 1, 2, 3])
  • new TypedArray(typedArray)
    • 创建一个相同长度的类型化数组,并复制其内容(复制底层 ArrayBuffer
  • new TypedArray(length?)
    • 创建类型化数组以包含这么多元素,默认为 0
    • 它的字节长度将是 length 乘以单个 TypedArray.BYTES_PER_ELEMENT 中的字节数

可以直接创建一个 TypedArray,而无需提及 ArrayBuffer。但是,视图离不开底层的 ArrayBuffer,因此,除第一种情况(已提供 ArrayBuffer)外,其他所有情况都会自动创建底层 ArrayBuffer

属性

如要访问底层的 ArrayBuffer,在 TypedArray 中有如下的属性:

  • buffer:引用底层 ArrayBuffer
  • byteLength:底层 ArrayBuffer 的长度(即:数据占用字节数)
  • byteOffset:当前视图相对底层 ArrayBuffer 的偏移值

其他属性:

  • length:包含多少元素
  • BYTES_PER_ELEMENT:每个元素占用的字节数

方法 ^1

TypedArray 具有和 Array 相同的方法设计,可以设置获取元素(arr[i]setget)、查找(indexOflastIndexOf)、填充(fill)、切片(slice)、反转(reverse)、排序(sort)、拼接字符串(join)、遍历(forEachmapfindreduce )等

但没有 pushpopshiftunshiftspliceconcat 等会改变数组长度的方法

还有两种 Array 没有的方法:

  • arr.set(fromArr, offset?):从 offset(默认为 0)开始,将 fromArr 中的所有元素复制到 arr
  • arr.subarray(begin?, end?):创建一个从 begin 到 end(包头不包尾)相同类型的新视图。这类似于 slice 方法,但不复制任何内容(只是创建一个新视图,以对给定片段的数据进行操作)

溢出处理

如果尝试将越界值写入类型化数组,多余的高位数据将会被忽略

如尝试将 256 放入 Uint8Array,则超出的最高位的 1 会被忽略,因此结果是 0:

Uint8ClampedArray 在这方面比较特殊,它的表现不太一样。对于大于 255 的任何数字,它将保存为 255,对于任何负数,它将保存为 0。此行为对于图像或颜色处理很有用

DataView

类型化数组限制了数据每个元素的字节大小,而 DataView 是在 ArrayBuffer 上的一种超灵活的“未类型化”视图,它允许以任何格式访问任何偏移量(offset)的数据

  • 类型化的数组:构造器决定了其格式,整个数组是统一的,第 i 个元素是 arr[i]
  • DataView:可以使用 .getUint8(offset) 或 .getUint16(offset) 之类的方法访问数据。在调用方法时选择格式,而不是在构造的时候

构造器

  • new DataView(buffer, byteOffset?, byteLength?)
    • bufferArrayBufferSharedArrayBuffer
    • byteOffset:视图的起始字节位置(即:跳过几个字节长度的数据),默认为 0
    • length:视图的字节长度,默认至 buffer 的末尾

DataView 与类型化数组不同,只有这一个构造器,即 DataView 不会自行创建缓冲区,需要事先准备好

属性

  • buffer:引用底层 ArrayBuffer
  • byteLength:底层 ArrayBuffer 的长度(即:数据占用字节数)
  • byteOffset:当前视图相对底层 ArrayBuffer 的偏移值

方法

没有与 ArrayTypedArray 相类型的方法,只有对不同偏移量读写的方法:

  • getXxx(byteOffset, littleEndian?):获取指定字节偏移量处的数据
  • setXxx(byteOffset, value, littleEndian?):设置指定字节偏移量处的数据
    • XxxInt8|Uint8|Int16|Uint16|Int32|Uint32|BigInt64|BigUint64|Float32|Float64 之一
    • littleEndian:采用何种字节序
      • false 或者未指定(undefined):大端序
      • true:小端序

字节序

字节序或字节顺序描述了计算机如何组织构成数字的字节

  • 小端序:从最低有效位到最高有效位的顺序存储字节(即:低位字节排放在内存的低地址端,高位字节排放在内存的高地址端)
    • 形象类比:31 December 2050
    • 现代电脑大部分使用小端序,所有的英特尔处理器都使用小端序
  • 大端序:从最高有效位到最低有效位的顺序存储字节(即:高位字节排放在内存的低地址端,低位字节排放在内存的高地址端)
    • 形象类比:2050-12-31
    • 互联网标准,从标准 Unix 套接字层开始,一直到标准化网络的二进制数据结构,通常要求数据使用大端序存储。此外,老式 Mac 计算机的 68000 系列和 PowerPC 微处理器曾使用大端序

例如,用不同字节序存储数字 0x12345678

  • 小端序:0x78 0x56 0x34 0x12
  • 大端序:0x12 0x34 0x56 0x78
  • 混合序(曾经的做法,非常罕见):0x34 0x12 0x78 0x56