写在前面
ArrayBuffer
是一种固定长度的二进制数据缓冲区。它可以用来在 JavaScript 中存储和操作大量的二进制数据。ArrayBuffer
本身不提供直接访问其内容的方法,而是通过其他对象(如 TypedArray
和 DataView
)来读取和写入数据。
以下是一个简单的示例,展示如何创建一个 ArrayBuffer
对象并填充它:
javascript
const buffer = new ArrayBuffer(16); // 创建一个 16 字节的 ArrayBuffer
const view = new Uint8Array(buffer); // 创建一个 Uint8Array 视图
for (let i = 0; i < buffer.byteLength; i++) {
view[i] = i * 2; // 填充数据
}
console.log(view); // [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
TypedArray 视图
TypedArray
是一种允许你在 ArrayBuffer
上创建不同类型的数组视图的对象。这些视图提供了对 ArrayBuffer
中数据的直接访问,并且可以以不同的方式解释数据(例如,作为无符号整数、有符号整数、浮点数等)。
以下是一些常见的 TypedArray
视图:
Int8Array
: 有符号 8 位整数Uint8Array
: 无符号 8 位整数Int16Array
: 有符号 16 位整数Uint16Array
: 无符号 16 位整数Int32Array
: 有符号 32 位整数Uint32Array
: 无符号 32 位整数Float32Array
: 32 位浮点数Float64Array
: 64 位浮点数
以下是一个示例,展示如何使用 TypedArray
视图来读取和写入 ArrayBuffer
中的数据:
javascript
const buffer = new ArrayBuffer(16);
const int16View = new Int16Array(buffer);
int16View[0] = 1;
int16View[1] = 2;
console.log(int16View); // [1, 2, 0, 0, 0, 0, 0, 0]
复合视图
复合视图是指在同一个 ArrayBuffer
上创建多个不同类型的 TypedArray
视图。这样可以方便地在同一块内存中存储和操作不同类型的数据。
以下是一个示例,展示如何在同一个 ArrayBuffer
上创建多个不同类型的视图:
javascript
const buffer = new ArrayBuffer(24);
const uint8View = new Uint8Array(buffer);
const uint16View = new Uint16Array(buffer);
const float32View = new Float32Array(buffer);
uint8View[0] = 1;
uint8View[1] = 2;
uint8View[2] = 3;
uint8View[3] = 4;
console.log(uint16View); // [258, 771, 0, 0]
console.log(float32View); // [1.401298464324817e-45, 5.605193857299227e-45, 0, 0]
DataView 视图
DataView
是一种更灵活的视图,可以在 ArrayBuffer
上读取和写入不同类型的数据,而不需要创建多个 TypedArray
视图。它提供了一组方法来读取和写入各种类型的数据,包括整数、浮点数和字符串。
以下是一个示例,展示如何使用 DataView
视图来读取和写入 ArrayBuffer
中的数据:
javascript
const buffer = new ArrayBuffer(16);
const dataView = new DataView(buffer);
dataView.setInt16(0, 1);
dataView.setFloat32(4, 3.14);
console.log(dataView.getInt16(0)); // 1
console.log(dataView.getFloat32(4)); // 3.14
二进制数组的应用
二进制数组在许多领域都有广泛的应用,例如:
- 图像和视频处理:可以使用二进制数组来存储和操作图像和视频数据。
- 网络通信:可以使用二进制数组来发送和接收二进制数据。
- 加密和解密:可以使用二进制数组来存储和操作加密和解密的数据。
- 游戏开发:可以使用二进制数组来存储和操作游戏中的数据。
以下是一个简单的示例,展示如何使用二进制数组来读取和显示一张 PNG 图像:
javascript
const img = document.createElement('img');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
fetch('image.png')
.then(response => response.arrayBuffer())
.then(buffer => {
const uint8View = new Uint8Array(buffer);
const pngData = uint8View.slice(0, 8); // PNG 文件头
if (pngData[0] === 137 && pngData[1] === 80 && pngData[2] === 78 && pngData[3] === 71 && pngData[4] === 13 && pngData[5] === 10 && pngData[6] === 26 && pngData[7] === 10) {
const pngDecoder = new PNGDecoder();
const decodedData = pngDecoder.decode(buffer);
canvas.width = decodedData.width;
canvas.height = decodedData.height;
ctx.putImageData(decodedData, 0, 0);
img.src = canvas.toDataURL();
document.body.appendChild(img);
} else {
console.error('Invalid PNG file');
}
});
SharedArrayBuffer
SharedArrayBuffer
是一种允许多个线程或 Web Worker 共享同一块内存的对象。它可以用来实现高效的并行计算和数据共享。
以下是一个简单的示例,展示如何使用 SharedArrayBuffer
在主线程和 Web Worker 之间共享数据:
javascript
// 主线程
const buffer = new SharedArrayBuffer(4);
const uint8View = new Uint8Array(buffer);
const worker = new Worker('worker.js');
worker.postMessage({ buffer });
// worker.js
self.onmessage = function(event) {
const buffer = event.data.buffer;
const uint8View = new Uint8Array(buffer);
// 在 worker 中修改数据
uint8View[0] = 1;
uint8View[1] = 2;
uint8View[2] = 3;
uint8View[3] = 4;
};
Atomics 对象
Atomics
是一个全局对象,提供了一组方法来执行原子操作(即不可分割的操作)在 SharedArrayBuffer
上。这些方法可以用来实现线程安全的并发操作。
以下是一个简单的示例,展示如何使用 Atomics
对象来实现一个线程安全的计数器:
javascript
// 主线程
const buffer = new SharedArrayBuffer(4);
const uint32View = new Uint32Array(buffer);
const worker1 = new Worker('worker1.js');
const worker2 = new Worker('worker2.js');
worker1.postMessage({ buffer });
worker2.postMessage({ buffer });
// worker1.js 和 worker2.js
self.onmessage = function(event) {
const buffer = event.data.buffer;
const uint32View = new Uint32Array(buffer);
for (let i = 0; i < 1000000; i++) {
Atomics.add(uint32View, 0, 1);
}
};
在这个示例中,两个 Web Worker 都在尝试增加同一个计数器的值。由于使用了 Atomics.add
方法,这个操作是原子的,因此不会出现数据竞争问题。