Published
Edited
Jun 9, 2020
Insert cell
md`# ArrayBuffer(二进制数组)`
Insert cell
md`
在 JavaScript 中有很多种二进制数据格式,仅举几个例子:
- ArrayBuffer
- Uint8Array
- DataView
- Blob
- File

** 基本的二进制对象是 ArrayBuffer —— 对固定长度的连续内存空间的引用。**
`
Insert cell
// 创建一个长度为 16 的 buffer
// 它会分配一个 16 字节的连续内存空间,并用 0 进行预填充。
buffer = new ArrayBuffer(16);
Insert cell
console.log(buffer.byteLength); // 16
Insert cell
md`
⚠️** ArrayBuffer 不是某种东西的数组 **</br>
让我们先澄清一个可能的误区。ArrayBuffer 与 Array 没有任何共同之处:
- 它的长度是固定的,我们无法增加或减少它的长度。
- 它正好占用了内存中的那么多空间。
- 要访问单个字节,需要另一个“视图”对象,而不是 buffer[index]`
Insert cell
md`
**如要操作 ArrayBuffer,我们需要使用“视图”对象。**
视图对象本身并不存储任何东西。它是一副“眼镜”,透过它来解释存储在 ArrayBuffer 中的字节。
- Uint8Array —— 将 ArrayBuffer 中的每个字节视为 0 到 255 之间的单个数字(每个字节是 8 位,因此只能容纳那么多)。这称为 “8 位无符号整数”。
- Uint16Array —— 将每 2 个字节视为一个 0 到 65535 之间的整数。这称为 “16 位无符号整数”。
- Uint32Array —— 将每 4 个字节视为一个 0 到 4294967295 之间的整数。这称为 “32 位无符号整数”。
- Float64Array —— 将每 8 个字节视为一个 5.0x10-324 到 1.8x10308 之间的浮点数。
`
Insert cell
{
let buffer = new ArrayBuffer(16); // 创建一个长度为 16 的 buffer
let view = new Uint32Array(buffer); // 将 buffer 视为一个 32 位整数的序列
console.log(Uint32Array.BYTES_PER_ELEMENT); // 每个整数 4 个字节
console.log(view.length); // 4,它存储了 4 个整数
console.log(view.byteLength); // 16,字节中的大小

// 让我们写入一个值
view[0] = 123456;

// 遍历值
for(let num of view) {
console.log(num); // 123456,然后 0,0,0(一共 4 个值)
}
}
Insert cell
md`
ArrayBuffer 上的视图之一的通用总称术语:TypedArray </br>
- new TypedArray(buffer, [byteOffset], [length]);
- new TypedArray(object);
- new TypedArray(typedArray);
- new TypedArray(length);
- new TypedArray();
`
Insert cell
md`
1. 如果给定的是 ArrayBuffer 参数,则会在其上创建视图
2. 如果给定的是 Array,或任何类数组对象,则会创建一个相同长度的类型化数组,并复制其内容
3. 如果给定的是另一个 TypedArray,也是建一个相同长度的类型化数组,并复制其内容
4. 对于数字参数 length —— 创建类型化数组以包含这么多元素
5. 不带参数的情况下,创建长度为零的类型化数组
`
Insert cell
md` 对于情况2`
Insert cell
{
let arr = new Uint8Array([0, 1, 2, 3]);
console.log( arr.length ); // 4,创建了相同长度的二进制数组
console.log( arr[1] ); // 1,用给定值填充了 4 个字节(无符号 8 位整数)
return arr;
}
Insert cell
md`对于情况3`
Insert cell
{
let arr16 = new Uint16Array([1, 1000]);
let arr8 = new Uint8Array(arr16);
console.log( arr8[0] ); // 1
console.log( arr8[1] ); // 232,试图复制 1000,但无法将 1000 放进 8 位字节中(详述见下文)。
return arr8;
}
Insert cell
md`对于情况4`
Insert cell
{
let arr = new Uint16Array(4); // 为 4 个整数创建类型化(Unit16)数组
console.log( Uint16Array.BYTES_PER_ELEMENT ); // 每个整数 2 个字节
console.log( arr.byteLength ); // 8(2* 4 字节中的大小)
}
Insert cell
md`越界行为`
Insert cell
md`
如果我们尝试将越界值写入类型化数组会出现什么情况?不会报错。但是多余的位被切除。</br>
例如,我们尝试将 256 放入 Uint8Array。256 的二进制格式是 100000000(9 位),但 Uint8Array 每个值只有 8 位,因此可用范围为 0 到 255。</br>
对于更大的数字(这里是256),仅存储最右边的(低位有效)8 位,其余部分被切除:
\`\`\`
1 00000000 // 0
\`\`\`
257 的二进制格式是 100000001(9 位),最右边的 8 位会被存储,因此数组中会有 1
\`\`\`
1 00000001 // 1
\`\`\`
换句话说,该数字对 28 取模的结果被保存了下来。
`
Insert cell
md`TypedArray 方法`
Insert cell
md`
TypedArray 具有常规的 Array 方法,但有个明显的例外。</br>
- 可用:(iterate),map,slice,find 和 reduce 等
- 不可用:splice(不可删除),concat
- 另外支持:arr.set[fromArr,[offset]],arr.subarray[begin,end]
`
Insert cell
md`DataView`
Insert cell
md`
DataView 是在 ArrayBuffer 上的一种特殊的超灵活“未类型化”视图。它允许以任何格式访问任何偏移量(offset)的数据。</br>
\`\`\`
new DataView(buffer, [byteOffset], [byteLength])
\`\`\`
- buffer —— 底层的 ArrayBuffer。与类型化数组不同,DataView 不会自行创建缓冲区(buffer)。我们需要事先准备好。
- byteOffset —— 视图的起始字节位置(默认为 0)。
- byteLength —— 视图的字节长度(默认至 buffer 的末尾)。
`
Insert cell
md`这里我们从同一个 buffer 中提取不同格式的数字`
Insert cell
{
// 4 个字节的二进制数组,每个都是最大值 255
let buffer = new Uint8Array([255, 255, 255, 255]).buffer;

let dataView = new DataView(buffer);

// 在偏移量为 0 处获取 8 位数字
console.log( dataView.getUint8(0) ); // 255

// 现在在偏移量为 0 处获取 16 位数字,它由 2 个字节组成,一起解析为 65535
console.log( dataView.getUint16(0) ); // 65535(最大的 16 位无符号整数)

// 在偏移量为 0 处获取 32 位数字
console.log( dataView.getUint32(0) ); // 4294967295(最大的 32 位无符号整数)

dataView.setUint32(0, 0); // 将 4 个字节的数字设为 0,即将所有字节都设为 0
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more