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.prototype
上push
、pop
等方法
类型化数组
ArrayBuffer
是一个内存区域,它里面存储了什么无从判断,只是一个原始的字节序列。想要操作 ArrayBuffer
,需要使用“视图”对象。视图对象本身并不存储任何东西,它是一副“眼镜”,透过它来解释存储在 ArrayBuffer
中的字节
类型化数组 | 每个元素所占字节数 | 描述(C 语言中的数据类型) |
---|---|---|
Int8Array | 1 | 8 位整型数(signed char ) |
UInt8Array | 1 | 8 位无符号整型数(unsigned char ) |
Uint8ClampedArray | 1 | 8 位无符号整型数,溢出时有特殊处理 |
Int16Array | 2 | 16 位整型数(signed short ) |
UInt16Array | 2 | 16 位无符号整型数(unsigned short ) |
Int32Array | 4 | 32 位整型数(signed int ) |
UInt32Array | 4 | 32 位无符号整型数(unsigned int ) |
BigInt64Array | 8 | 64 位整型数(signed long / long long ) |
BigUint64Array | 8 | 64 位整型数(unsigned long / long long ) |
Float32Array | 4 | 单精度 32 位浮点数(float ) |
Float64Array | 8 | 双精度 64 位浮点数(double ) |
构造器
书写
TypedArray
的说明没有名为
TypedArray
的构造器,它只是表示ArrayBuffer
上的视图之一的通用总称术语当你看到
new TypedArray
之类的内容时,它表示new Int8Array
、new Uint8Array
及其他中之一
new TypedArray(buffer, byteOffset?, length?)
buffer
:ArrayBuffer
或SharedArrayBuffer
byteOffset
:视图的起始字节位置(即:跳过几个字节长度的数据),默认为 0length
:视图的字节长度,默认至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]
、set
、get
)、查找(indexOf
、lastIndexOf
)、填充(fill
)、切片(slice
)、反转(reverse
)、排序(sort
)、拼接字符串(join
)、遍历(forEach
、map
、find
、reduce
)等
但没有 push
、pop
、shift
、unshift
、splice
、concat
等会改变数组长度的方法
还有两种 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?)
buffer
:ArrayBuffer
或SharedArrayBuffer
byteOffset
:视图的起始字节位置(即:跳过几个字节长度的数据),默认为 0length
:视图的字节长度,默认至buffer
的末尾
DataView
与类型化数组不同,只有这一个构造器,即 DataView
不会自行创建缓冲区,需要事先准备好
属性
buffer
:引用底层ArrayBuffer
byteLength
:底层ArrayBuffer
的长度(即:数据占用字节数)byteOffset
:当前视图相对底层ArrayBuffer
的偏移值
方法
没有与 Array
或 TypedArray
相类型的方法,只有对不同偏移量读写的方法:
getXxx(byteOffset, littleEndian?)
:获取指定字节偏移量处的数据setXxx(byteOffset, value, littleEndian?)
:设置指定字节偏移量处的数据Xxx
:Int8|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