ArrayBuffer 实践

本文详细介绍了JavaScript中的ArrayBuffer、TypedArray视图和DataView的使用,包括字节序、复合视图的概念及其在AJAX、WebSocket中的应用。通过ArrayBuffer和视图,可以高效地处理二进制数据,实现数据的读写和转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ArrayBuffer 实践

ArrayBuffer对象、TypedArray视图和DataView视图是 JavaScript 操作二进制数据的一个接口。都是以数组的语法处理二进制数据,所以统称为二进制数组。主要用于二进制数据的通信。

ArrayBuffer 以数组的形式直接操作内存,提升了脚本的性能。二进制数组由三类对象组成。

  • ArrayBuffer 对象
  • TypedArray 视图
  • DataView 视图

ArrayBuffer 对象使用

ArrayBuffer对象代表储存二进制数据的一段内存,它不能直接读写,只能通过视图(TypedArray视图和DataView视图)来读写,视图的作用是以指定格式解读二进制数据。

注意: ArrayBuffer 不能直接读写,必须通过 TypedArray 或 DataView才能进行读写。

// 创建 ArrayBuffer, 分配一段连续内存区域, 默认值为0
const buf = new ArrayBuffer(32);
// 获取 ArrayBuffer 长度
console.log(buf.byteLength); // 32
// ArrayBuffer 拷贝
const newBuffer = buf.slice(0, 3); // 从 0 ~ 3(不包括3拷贝)
// 判断是否是视图
const buffer = new ArrayBuffer(8);
ArrayBuffer.isView(buffer) // false

TypedArray 视图

同一段内存,不同数据有不同的解读方式,这就叫做“视图”(view)。

ArrayBuffer有两种视图,一种是TypedArray视图,另一种是DataView视图。前者的数组成员都是同一个数据类型,后者的数组成员可以是不同的数据类型。

目前,TypedArray视图一共包括 9 种类型,每一种视图都是一种构造函数。

  • Int8Array:8 位有符号整数,长度 1 个字节。
  • Uint8Array:8 位无符号整数,长度 1 个字节。
  • Uint8ClampedArray:8 位无符号整数,长度 1 个字节,溢出处理不同。
  • Int16Array:16 位有符号整数,长度 2 个字节。
  • Uint16Array:16 位无符号整数,长度 2 个字节。
  • Int32Array:32 位有符号整数,长度 4 个字节。
  • Uint32Array:32 位无符号整数,长度 4 个字节。
  • Float32Array:32 位浮点数,长度 4 个字节。
  • Float64Array:64 位浮点数,长度 8 个字节。
// 创建 TypedArray
const buffer = new ArrayBuffer(8);
const typed = new Int32Array(buffer);
// 普通数组的操作方法和属性,对 TypedArray 数组完全适用。
let ui8 = Uint8Array.of(0, 1, 2);
for (let byte of ui8) {
  console.log(byte);
}
// 0 1 2
// BYTES_PER_ELEMENT属性,表示数据类型占据的字节数。
Int8Array.BYTES_PER_ELEMENT // 1
Uint8Array.BYTES_PER_ELEMENT // 1
Uint8ClampedArray.BYTES_PER_ELEMENT // 1
// buffer属性,返回整段内存区域对应的ArrayBuffer对象。该属性为只读属性。
const a = new Float32Array(64);
const b = new Uint8Array(a.buffer);
// byteLength属性返回 TypedArray 数组占据的内存长度,单位为字节。
// byteOffset属性返回 TypedArray 数组从底层ArrayBuffer对象的哪个字节开始。这两个属性都是只读属性。
// length属性表示 TypedArray 数组含有多少个成员。注意将 length 属性是成员长度
const a = new Int16Array(8);
a.length // 8
a.byteLength // 16

字节序

字节序指的是数值在内存中的表示方式。涉及到大端对齐和小端对齐。

  • 小端对齐采用小端字节序。比如,一个占据四个字节的 16 进制数0x12345678,决定其大小的最重要的字节是“12”,最不重要的是“78”。小端字节序将最不重要的字节排在前面,储存顺序就是78563412
  • 大端对齐采用大端字节序。大端字节序则完全相反,将最重要的字节排在前面,储存顺序就是12345678
  • 目前所有个人电脑几乎都是小端字节序,所以 TypedArray 数组内部也采用小端字节序读写数据。
  • 对于采用大端对齐的操作系统,TypedArray 数组将无法正确解析。因此需要引入可以设置字节序的 DataView

案例:

buffer: [0x02, 0x01, 0x03, 0x07] 
转变为小端对齐结果: 0x07030102 
转变为大端对齐结果: 0x02010307

小端对齐: 0xff050210
转变为buffer结果: [0x10, 0x02, 0x05, 0xff]

大端对齐: 0xff050210
转变为buffer结果: [0xff, 0x05, 0x02, 0x10]

判断当前视图是小端字节序,还是大端字节序

const BIG_ENDIAN = Symbol('BIG_ENDIAN');
const LITTLE_ENDIAN = Symbol('LITTLE_ENDIAN');

function getPlatformEndianness() {
    let arr32 = Uint32Array.of(0x12345678);
    let arr8 = new Uint8Array(arr32.buffer);
    switch ((arr8[0]*0x1000000) + (arr8[1]*0x10000) + (arr8[2]*0x100) + (arr8[3])) {
        case 0x12345678:
            return BIG_ENDIAN;
        case 0x78563412:
            return LITTLE_ENDIAN;
        default:
            throw new Error('Unknown endianness');
    }
}

复合视图

视图的构造函数可以指定起始位置和长度,所以在同一段内存之中,可以依次存放不同类型的数据,这叫做“复合视图”。

const buffer = new ArrayBuffer(24);
// 将 ArrayBuffer 分成三个部分:
const idView = new Uint32Array(buffer, 0, 1);
const usernameView = new Uint8Array(buffer, 4, 16);
const amountDueView = new Float32Array(buffer, 20, 1);

DataView 视图

如果一段数据包括多种类型(比如服务器传来的 HTTP 数据),这时除了建立ArrayBuffer对象的复合视图以外,还可以通过DataView视图进行操作。DataView视图的设计目的,是用来处理网络设备传来的数据,所以大端字节序或小端字节序是可以自行设定的。

// 构造函数
const dataView = new DataView(ArrayBuffer buffer [, 字节起始位置 [, 长度]]);
// 长度属性
console.log(dataView.buffer); // 返回对应的 ArrayBuffer 对象
console.log(dataView.byteLength); // 返回占据的内存字节长度
console.log(dataView.byteOffset); // 返回当前视图从对应的 ArrayBuffer 对象的哪个字节开始

DataView实例提供 8 个方法读取内存。用 set 方法写入内存

  • getInt8:读取 1 个字节,返回一个 8 位整数。
  • getUint8:读取 1 个字节,返回一个无符号的 8 位整数。
  • getInt16:读取 2 个字节,返回一个 16 位整数。
  • getUint16:读取 2 个字节,返回一个无符号的 16 位整数。
  • getInt32:读取 4 个字节,返回一个 32 位整数。
  • getUint32:读取 4 个字节,返回一个无符号的 32 位整数。
  • getFloat32:读取 4 个字节,返回一个 32 位浮点数。
  • getFloat64:读取 8 个字节,返回一个 64 位浮点数。

应用

AJAX

let xhr = new XMLHttpRequest();
xhr.open('GET', someUrl);
// 设置返回值类型为二进制数据类型,如果不知道类型,可以设为 blob
xhr.responseType = 'arraybuffer';
// xhr.readyState == 4 时执行
xhr.onload = function () {
    // 回传32 位整数时处理
    const arrayResponse = xhr.response;
    const dataView = new DataView(arrayResponse);
    const ints = new Uint32Array(dataView.byteLength / 4);

    xhrDiv.style.backgroundColor = "#00FF00";
    xhrDiv.innerText = "Array is " + ints.length + "uints long";
};

xhr.send();

WebSocket

WebSocket可以通过ArrayBuffer,发送或接收二进制数据。

let socket = new WebSocket('ws://127.0.0.1:8081');
socket.binaryType = 'arraybuffer';
// 等待 socket 连接创建
socket.addEventListener('open', function (event) {
  // 发送二进制数据
  const typedArray = new Uint8Array(4);
  socket.send(typedArray.buffer);
});

// 接收二进制数据
socket.addEventListener('message', function (event) {
  const arrayBuffer = event.data;
  // ···
});

将 ArrayBuffer 中数据转 utf-8

// 0x4F60;0x597D;0x554A; => 你好啊
const array = new ArrayBuffer(6);
const type = new Int16Array(array);
type[0] = 0x4F60;
type[1] = 0x597D;
type[2] = 0x554A;
let str = '';
for(let c of type) {
    str += String.fromCharCode(c);
}
console.log(str);

参考资料

ArrayBuffer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值