Skip to content

TypedArray 类型化数组

TypedArray 是 ES6 引入的用于处理二进制数据的数组类型,在 WebGL、Canvas、文件处理等场景中非常重要。

为什么需要 TypedArray#

普通数组可以存储任意类型,但处理二进制数据效率较低:

// 普通数组:每个元素可以是任意类型
const arr = [1, 'hello', { x: 1 }, 3.14]
// TypedArray:每个元素类型固定,内存连续
const uint8 = new Uint8Array([1, 2, 3, 4])
// 每个元素占用 1 字节,值范围 0-255

ArrayBuffer#

ArrayBuffer 是固定长度的二进制数据缓冲区:

// 创建 16 字节的缓冲区
const buffer = new ArrayBuffer(16)
console.log(buffer.byteLength) // 16
// ArrayBuffer 不能直接操作,需要通过视图
// buffer[0] = 1; // 不起作用
// 检查是否是 ArrayBuffer
ArrayBuffer.isView(buffer) // false

TypedArray 类型#

ES6 提供了 9 种 TypedArray 类型:

类型字节范围描述
Int8Array1-128 ~ 1278位有符号整数
Uint8Array10 ~ 2558位无符号整数
Uint8ClampedArray10 ~ 2558位无符号整数(溢出钳位)
Int16Array2-32768 ~ 3276716位有符号整数
Uint16Array20 ~ 6553516位无符号整数
Int32Array4-2^31 ~ 2^31-132位有符号整数
Uint32Array40 ~ 2^32-132位无符号整数
Float32Array4IEEE 75432位浮点数
Float64Array8IEEE 75464位浮点数

创建 TypedArray#

// 方式1:指定长度
const int8 = new Int8Array(4)
console.log(int8) // Int8Array [0, 0, 0, 0]
// 方式2:从数组创建
const uint8 = new Uint8Array([1, 2, 3, 4])
console.log(uint8) // Uint8Array [1, 2, 3, 4]
// 方式3:从 ArrayBuffer 创建
const buffer = new ArrayBuffer(8)
const int16 = new Int16Array(buffer)
console.log(int16.length) // 4(8字节/2字节每元素)
// 方式4:从 ArrayBuffer 的一部分创建
const view = new Int32Array(buffer, 0, 2) // 偏移0,长度2
// 方式5:从另一个 TypedArray 创建
const float32 = new Float32Array(uint8)

基本操作#

const arr = new Uint8Array(4)
// 读写元素
arr[0] = 255
arr[1] = 128
console.log(arr[0]) // 255
// 溢出处理
arr[2] = 256 // Uint8 最大 255
console.log(arr[2]) // 0(256 % 256)
arr[3] = -1
console.log(arr[3]) // 255(-1 在无符号中表示最大值)
// 属性
arr.length // 4
arr.byteLength // 4
arr.buffer // ArrayBuffer
arr.byteOffset // 0

Uint8ClampedArray 特殊性#

溢出时钳位到边界值而不是取模:

const uint8 = new Uint8Array([256, -1])
console.log(uint8) // [0, 255](取模)
const clamped = new Uint8ClampedArray([256, -1])
console.log(clamped) // [255, 0](钳位)
// 常用于图像处理,避免颜色值溢出

数组方法#

TypedArray 支持大部分数组方法:

const arr = new Uint8Array([3, 1, 4, 1, 5, 9])
// 支持的方法
arr.slice(0, 3) // Uint8Array [3, 1, 4]
arr.map((x) => x * 2) // Uint8Array [6, 2, 8, 2, 10, 18]
arr.filter((x) => x > 2) // Uint8Array [3, 4, 5, 9]
arr.reduce((a, b) => a + b, 0) // 23
arr.find((x) => x > 5) // 9
arr.indexOf(4) // 2
arr.includes(5) // true
arr.sort() // Uint8Array [1, 1, 3, 4, 5, 9]
// 不支持的方法(会改变长度)
// arr.push(1); // 不存在
// arr.pop(); // 不存在
// arr.splice(); // 不存在

TypedArray 特有方法#

const arr1 = new Uint8Array([1, 2, 3, 4])
const arr2 = new Uint8Array([5, 6])
// set:复制数据到指定位置
arr1.set(arr2, 2)
console.log(arr1) // [1, 2, 5, 6]
// subarray:创建视图(共享内存)
const sub = arr1.subarray(1, 3)
console.log(sub) // [2, 5]
sub[0] = 99
console.log(arr1) // [1, 99, 5, 6](原数组也变了)

DataView#

DataView 提供更灵活的二进制数据访问,支持:

const buffer = new ArrayBuffer(8)
const view = new DataView(buffer)
// 写入数据
view.setInt8(0, 127) // 位置0,写入int8
view.setInt16(1, 1000, true) // 位置1,写入int16,小端
view.setFloat32(4, 3.14, true) // 位置4,写入float32,小端
// 读取数据
view.getInt8(0) // 127
view.getInt16(1, true) // 1000
view.getFloat32(4, true) // 3.140000104904175

字节序#

const buffer = new ArrayBuffer(4)
const view = new DataView(buffer)
// 写入 0x12345678
view.setUint32(0, 0x12345678, false) // 大端(网络字节序)
new Uint8Array(buffer) // [0x12, 0x34, 0x56, 0x78]
view.setUint32(0, 0x12345678, true) // 小端(x86)
new Uint8Array(buffer) // [0x78, 0x56, 0x34, 0x12]

实际应用#

图像处理#

// 获取 Canvas 像素数据
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
const pixels = imageData.data // Uint8ClampedArray
// 反转颜色
for (let i = 0; i < pixels.length; i += 4) {
pixels[i] = 255 - pixels[i] // R
pixels[i + 1] = 255 - pixels[i + 1] // G
pixels[i + 2] = 255 - pixels[i + 2] // B
// pixels[i + 3] 是 Alpha,保持不变
}
ctx.putImageData(imageData, 0, 0)

文件处理#

// 读取文件为 ArrayBuffer
async function readFile(file) {
const buffer = await file.arrayBuffer()
return new Uint8Array(buffer)
}
// 检查 PNG 文件签名
function isPNG(buffer) {
const signature = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]
const header = new Uint8Array(buffer, 0, 8)
return signature.every((byte, i) => byte === header[i])
}

Base64 编解码#

// ArrayBuffer 转 Base64
function arrayBufferToBase64(buffer) {
const bytes = new Uint8Array(buffer)
let binary = ''
for (const byte of bytes) {
binary += String.fromCharCode(byte)
}
return btoa(binary)
}
// Base64 转 ArrayBuffer
function base64ToArrayBuffer(base64) {
const binary = atob(base64)
const buffer = new ArrayBuffer(binary.length)
const bytes = new Uint8Array(buffer)
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i)
}
return buffer
}

网络通信#

// 构建二进制协议数据包
function createPacket(type, payload) {
const payloadBytes = new TextEncoder().encode(payload)
const buffer = new ArrayBuffer(4 + payloadBytes.length)
const view = new DataView(buffer)
view.setUint8(0, type) // 消息类型
view.setUint8(1, 0) // 版本
view.setUint16(2, payloadBytes.length, true) // 长度(小端)
new Uint8Array(buffer, 4).set(payloadBytes)
return buffer
}
// 解析数据包
function parsePacket(buffer) {
const view = new DataView(buffer)
const type = view.getUint8(0)
const version = view.getUint8(1)
const length = view.getUint16(2, true)
const payload = new TextDecoder().decode(new Uint8Array(buffer, 4, length))
return { type, version, payload }
}

WebGL 顶点数据#

// 创建顶点缓冲
const vertices = new Float32Array([
// x, y, z
-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0,
])
// 创建索引缓冲
const indices = new Uint16Array([0, 1, 2])
// 传递给 WebGL
const gl = canvas.getContext('webgl')
const vertexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

性能优势#

// 普通数组 vs TypedArray 性能对比
const size = 1000000
// 普通数组
console.time('Array')
const arr = new Array(size)
for (let i = 0; i < size; i++) arr[i] = i
let sum1 = 0
for (let i = 0; i < size; i++) sum1 += arr[i]
console.timeEnd('Array')
// TypedArray
console.time('TypedArray')
const typed = new Int32Array(size)
for (let i = 0; i < size; i++) typed[i] = i
let sum2 = 0
for (let i = 0; i < size; i++) sum2 += typed[i]
console.timeEnd('TypedArray')
// TypedArray 通常快 2-10 倍

小结#

类型用途
ArrayBuffer底层二进制数据容器
TypedArray类型化视图,高效操作同类型数据
DataView灵活视图,支持混合类型和字节序

TypedArray 是处理二进制数据的利器,在图像处理、音视频、网络通信、WebGL 等场景中不可或缺。