icon
类型化数组

类型化数组

类型化数组(Typed Arrays)提供了操作二进制数据的机制,用于处理音频、视频、图像等二进制数据。

基本概念

类型化数组由两部分组成:

  1. ArrayBuffer:存储二进制数据的缓冲区
  2. 视图(View):解释缓冲区数据的类型化数组
// 创建 ArrayBuffer(16 字节)
const buffer = new ArrayBuffer(16);

// 创建视图(32 位整数数组,4 个元素)
const view = new Int32Array(buffer);
console.log(view.length); // 4

ArrayBuffer

ArrayBuffer 是固定长度的原始二进制数据缓冲区。

// 创建 16 字节的缓冲区
const buffer = new ArrayBuffer(16);
console.log(buffer.byteLength); // 16

// 不能直接操作 ArrayBuffer,需要通过视图
// buffer[0] = 1; // 错误

类型化数组视图

| 类型 | 字节 | 范围 | |------|------|------| | Int8Array | 1 | -128 到 127 | | Uint8Array | 1 | 0 到 255 | | Uint8ClampedArray | 1 | 0 到 255(截断) | | Int16Array | 2 | -32768 到 32767 | | Uint16Array | 2 | 0 到 65535 | | Int32Array | 4 | -2^31 到 2^31-1 | | Uint32Array | 4 | 0 到 2^32-1 | | Float32Array | 4 | 32 位浮点数 | | Float64Array | 8 | 64 位浮点数 |

创建类型化数组

  1. 从长度创建
const arr = new Int32Array(4); // 创建 4 个 32 位整数
console.log(arr.length);       // 4
console.log(arr.byteLength);  // 16 (4 * 4 字节)
  1. 从数组创建
const arr = new Int32Array([1, 2, 3, 4]);
console.log(arr); // Int32Array [1, 2, 3, 4]
  1. 从 ArrayBuffer 创建
const buffer = new ArrayBuffer(16);
const view = new Int32Array(buffer);
view[0] = 1;
view[1] = 2;
console.log(view); // Int32Array [1, 2, 0, 0]
  1. 从另一个类型化数组创建
const source = new Int32Array([1, 2, 3, 4]);
const copy = new Int32Array(source);
console.log(copy); // Int32Array [1, 2, 3, 4]

DataView

DataView 提供了更灵活的缓冲区访问方式,可以指定字节序。

const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);

// 写入数据(大端序)
view.setInt32(0, 0x12345678, false); // false = 大端序
view.setInt32(4, 0x9ABCDEF0, false);

// 读取数据
console.log(view.getInt32(0, false).toString(16)); // '12345678'
console.log(view.getInt32(4, false).toString(16)); // '9abcdef0'

// 小端序
view.setInt32(0, 0x12345678, true); // true = 小端序
console.log(view.getInt32(0, true).toString(16)); // '78563412'

实际应用

  1. 图像数据处理
// 创建 RGBA 图像数据(100x100 像素)
const width = 100;
const height = 100;
const imageData = new Uint8ClampedArray(width * height * 4);

// 设置像素颜色(红色)
for (let i = 0; i < imageData.length; i += 4) {
  imageData[i] = 255;     // R
  imageData[i + 1] = 0;   // G
  imageData[i + 2] = 0;   // B
  imageData[i + 3] = 255; // A
}
  1. 音频数据处理
// 创建音频缓冲区(44100 Hz,1 秒,单声道)
const sampleRate = 44100;
const duration = 1; // 秒
const audioBuffer = new Float32Array(sampleRate * duration);

// 生成正弦波
const frequency = 440; // A4 音符
for (let i = 0; i < audioBuffer.length; i++) {
  audioBuffer[i] = Math.sin(2 * Math.PI * frequency * i / sampleRate);
}
  1. 网络数据传输
// 发送二进制数据
async function sendBinaryData(data) {
  const buffer = new ArrayBuffer(data.length);
  const view = new Uint8Array(buffer);
  
  for (let i = 0; i < data.length; i++) {
    view[i] = data.charCodeAt(i);
  }
  
  await fetch('/api/upload', {
    method: 'POST',
    body: buffer
  });
}
  1. 文件读取
async function readFileAsArrayBuffer(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsArrayBuffer(file);
  });
}

// 使用
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async (e) => {
  const file = e.target.files[0];
  const buffer = await readFileAsArrayBuffer(file);
  const view = new Uint8Array(buffer);
  console.log('文件大小:', view.length, '字节');
});
  1. WebGL 缓冲区
// 创建顶点位置数据
const vertices = new Float32Array([
  -1.0, -1.0, 0.0,  // 顶点 1
   1.0, -1.0, 0.0,  // 顶点 2
   0.0,  1.0, 0.0   // 顶点 3
]);

// 创建 WebGL 缓冲区
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  1. 数据转换
// 字符串转 ArrayBuffer
function stringToArrayBuffer(str) {
  const buffer = new ArrayBuffer(str.length);
  const view = new Uint8Array(buffer);
  for (let i = 0; i < str.length; i++) {
    view[i] = str.charCodeAt(i);
  }
  return buffer;
}

// ArrayBuffer 转字符串
function arrayBufferToString(buffer) {
  const view = new Uint8Array(buffer);
  let str = '';
  for (let i = 0; i < view.length; i++) {
    str += String.fromCharCode(view[i]);
  }
  return str;
}

数组方法

类型化数组支持大部分数组方法:

const arr = new Int32Array([1, 2, 3, 4, 5]);

// map
const doubled = arr.map(x => x * 2);
console.log(doubled); // Int32Array [2, 4, 6, 8, 10]

// filter
const evens = arr.filter(x => x % 2 === 0);
console.log(evens); // Int32Array [2, 4]

// reduce
const sum = arr.reduce((acc, x) => acc + x, 0);
console.log(sum); // 15

// forEach
arr.forEach((x, i) => {
  console.log(`arr[${i}] = ${x}`);
});

// find
const found = arr.find(x => x > 3);
console.log(found); // 4

// slice
const sliced = arr.slice(1, 3);
console.log(sliced); // Int32Array [2, 3]

注意事项

  1. 固定长度
const arr = new Int32Array(4);
arr.length = 5; // 无效,长度固定
console.log(arr.length); // 4
  1. 字节序
// 类型化数组使用平台字节序
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setInt32(0, 0x12345678, true); // 小端序

// 读取时需要注意字节序
console.log(view.getInt32(0, true).toString(16)); // '12345678'
  1. 溢出处理
// Uint8Array 溢出会取模
const arr = new Uint8Array(1);
arr[0] = 300;
console.log(arr[0]); // 44 (300 % 256)

// Uint8ClampedArray 会截断到 0-255
const clamped = new Uint8ClampedArray(1);
clamped[0] = 300;
console.log(clamped[0]); // 255
  1. 性能
// 类型化数组性能更好
const typed = new Float32Array(1000000);
const normal = new Array(1000000);

// 类型化数组操作更快
for (let i = 0; i < typed.length; i++) {
  typed[i] = i;
}
  1. 共享缓冲区
const buffer = new ArrayBuffer(16);
const view1 = new Int32Array(buffer);
const view2 = new Int32Array(buffer);

view1[0] = 42;
console.log(view2[0]); // 42 (共享同一缓冲区)

常用操作

  1. 复制数据
const source = new Int32Array([1, 2, 3, 4]);
const target = new Int32Array(source.length);
target.set(source);
console.log(target); // Int32Array [1, 2, 3, 4]
  1. 子数组
const arr = new Int32Array([1, 2, 3, 4, 5]);
const sub = arr.subarray(1, 3);
console.log(sub); // Int32Array [2, 3]

// 修改子数组会影响原数组
sub[0] = 99;
console.log(arr); // Int32Array [1, 99, 3, 4, 5]
  1. 转换为普通数组
const typed = new Int32Array([1, 2, 3]);
const normal = Array.from(typed);
// 或
const normal2 = [...typed];
  1. 检查类型
const arr = new Int32Array([1, 2, 3]);
console.log(arr instanceof Int32Array); // true
console.log(ArrayBuffer.isView(arr));  // true

最佳实践

  1. 选择合适的类型
// 图像数据使用 Uint8ClampedArray
const imageData = new Uint8ClampedArray(width * height * 4);

// 音频数据使用 Float32Array
const audioData = new Float32Array(sampleRate * duration);

// 整数计算使用 Int32Array
const integers = new Int32Array([1, 2, 3]);
  1. 重用缓冲区
// 创建可重用的缓冲区
const buffer = new ArrayBuffer(1024);
const view1 = new Int32Array(buffer, 0, 256);  // 前 256 个整数
const view2 = new Int32Array(buffer, 1024, 256); // 后 256 个整数
  1. 使用 DataView 处理多字节数据
// 需要控制字节序时使用 DataView
const view = new DataView(buffer);
view.setInt32(0, value, false); // 大端序