在 ES10 中,JavaScript 引入了 ArrayBuffer 对象和各种 TypedArray 类型的数据格式,这些新的数据类型给前端开发带来了更强大的数据存储和处理能力。本文将介绍 ArrayBuffer 和 TypedArray 的基本使用方法,以及它们在实际项目开发中的运用技巧。
ArrayBuffer 对象
ArrayBuffer 对象代表了一段内存块,它类似于 C 语言中的指针和内存地址,可以用来存储二进制数据。我们可以通过创建一个指定大小的 ArrayBuffer 对象来存储一段连续的内存块。
const buffer = new ArrayBuffer(32); // 开辟一个长度为 32 字节的内存块
上面的代码表示创建了一个 32 字节长的 ArrayBuffer 对象,具体来说就是在内存中开辟了一个 32 个字节大小的连续区域。
如果我们想要操作这个 ArrayBuffer 中的数据,就需要使用 TypedArray。
TypedArray
TypedArray 是 JavaScript 的一种特殊数组类型,它能够让我们像操作其它普通数组一样操作二进制数据。TypedArray 实际上是一种包装器,它封装了 ArrayBuffer 对象,并且提供了基于 ArrayBuffer 对象的视图接口,这样我们可以以不同的方式解释 ArrayBuffer 中的二进制数据。
JavaScript 中支持以下 9 种 TypedArray 类型:
- Int8Array:8 位有符号整数数组
- Uint8Array:8 位无符号整数数组
- Uint8ClampedArray:8 位无符号整数数组,溢出时自动截断
- Int16Array:16 位有符号整数数组
- Uint16Array:16 位无符号整数数组
- Int32Array:32 位有符号整数数组
- Uint32Array:32 位无符号整数数组
- Float32Array:32 位浮点数数组
- Float64Array:64 位浮点数数组
我们可以通过以下方式,使用 TypedArray 和 ArrayBuffer:
const buffer = new ArrayBuffer(32); const arr = new Int8Array(buffer);
上面的代码表示,我们使用 new ArrayBuffer(32)
创建了一个长度为 32 的内存块,然后使用 new Int8Array(buffer)
创建了一个基于 buffer 的 TypedArray,类型为 Int8Array,即每个元素都是一个字节长的有符号整数。
对于不同的数据类型,TypedArray 提供了不同的构造函数,其语法与传统数组不同,如以下示例:
const intArr = new Int16Array([1, 2, 3]); const floatArr = new Float32Array([1.2, 3.4, 5.6]);
上述示例中,new Int16Array([1, 2, 3])
表示创建一个类型为 Int16Array,长度为 3 的数组,元素为 1, 2, 3。而 new Float32Array([1.2, 3.4, 5.6])
则表示创建一个类型为 Float32Array,长度为 3 的数组,元素为 1.2, 3.4, 5.6。
TypedArray 对象的属性和方法
TypedArray 提供了一些常用的属性和方法,可以让我们方便地访问和操作底层的二进制数据。
length
TypedArray 对象的 length 属性表示存储在该 TypedArray 中的元素数量,即该 TypedArray 的长度。
buffer
TypedArray 对象的 buffer 属性表示该 TypedArray 对象所关联的 ArrayBuffer 对象。
byteOffset
TypedArray 对象的 byteOffset 属性表示该 TypedArray 对象相对于 buffer 的起始位置的偏移量,以字节为单位。
const buffer = new ArrayBuffer(32); const arr = new Int8Array(buffer, 8, 16); console.log(arr.byteOffset); // 打印 8
上述示例中,我们使用 new Int8Array(buffer, 8, 16)
创建了一个基于 buffer,类型为 Int8Array,偏移量为 8,长度为 16 的 TypedArray,因此 arr.byteOffset
的值为 8。
subarray()
TypedArray 对象的 subarray() 方法可以返回一个新的 TypedArray,其元素是从 start 位置开始,到 end 位置结束的一个子集。
const arr = new Int8Array([1, 2, 3, 4, 5, 6]); const subArr = arr.subarray(2, 4); console.log(subArr); // 打印 Int8Array [3, 4]
上述示例中,我们使用 new Int8Array([1, 2, 3, 4, 5, 6])
创建了一个类型为 Int8Array,长度为 6 的数组,并使用 arr.subarray(2, 4)
截取了从第 3 个元素开始(索引为2),到第 5 个元素结束(索引为3)的子集,即 [3, 4]。
set()
TypedArray 对象的 set() 方法可以让我们将一个 TypedArray 或者 array-like 对象的元素设置到当前 TypedArray 对象中。
const a = new Uint8Array([1, 2, 3]); const b = new Uint8Array([4, 5, 6]); const c = new Uint8Array(6); c.set(a); c.set(b, 3); console.log(c); // 打印 Uint8Array [1, 2, 3, 4, 5, 6]
上述示例中,我们创建了两个长度为 3 的 Uint8Array,分别是 a 和 b。然后我们创建了一个长度为 6 的 Uint8Array,用 c 来存储最终的结果。我们依次使用 c.set(a)
和 c.set(b, 3)
将 a 和 b 中的元素分别写进 c 中,最后打印出的结果即为 [1, 2, 3, 4, 5, 6]。
slice()
TypedArray 对象的 slice() 方法可以返回一个新的 TypedArray,包含其指定的起始位置和结束位置之间的元素,不包括结束位置的元素。
// javascriptcn.com 代码示例 const arr = new Int8Array(5); arr[0] = 1; arr[1] = 2; arr[2] = 3; arr[3] = 4; arr[4] = 5; const subArr = arr.slice(1, 4); console.log(subArr); // 打印 Int8Array [2, 3, 4]
上述示例中,我们创建了一个长度为 5 的 Int8Array,存储了 1 到 5 五个数字。然后,我们使用 arr.slice(1, 4)
操作将 arr 中索引为 1 到 3 的值(即 2, 3, 4)取出并创建子 TypedArray,最终打印结果为 [2, 3, 4]。
实践案例
下面是一个实际案例,我们使用 ArrayBuffer 和 TypedArray 来读取一个二进制文件,解析出其中的数据,并计算其校验和。
// javascriptcn.com 代码示例 // 以一个 PBM 图像文件为例,该文件将所有像素存储为 1 或 0,先读取文件(node.js 环境下): const fs = require('fs'); const data = fs.readFileSync('./test.pbm'); // 解析文件头信息 const magic = data.slice(0, 2).toString(); const width = parseInt(data.slice(3, 6).toString()); const height = parseInt(data.slice(7, 10).toString()); const expectedLine = `P1\n${width} ${height}\n`; // 检查文件头是否符合要求 if (magic !== 'P1' || data.slice(0, expectedLine.length).toString() !== expectedLine) { console.log('Invalid file content'); return; } // 从第一行像素数据开始解析文件 let index = expectedLine.length; // 创建 1 位长的二进制数组 const binaryArr = new Uint8Array((width / 8 + 1) * height); // 将像素数据写入二进制数组中 for (let row = 0; row < height; row++) { for (let col = 0; col < width; col++) { if (col % 8 === 0) { binaryArr[row * (width / 8 + 1) + col / 8] = 0; } if (data[index++] === 49) { // ASCII 码中数字 1 的编码为 49 binaryArr[row * (width / 8 + 1) + Math.floor(col / 8)] |= (0x80 >> (col % 8)); } } } // 计算校验和 let checksum = 0; for (let i = 0; i < binaryArr.length; i++) { checksum ^= binaryArr[i]; } console.log(`Checksum: ${checksum}`);
上述代码通过使用 ArrayBuffer 和 TypedArray 读取一个二进制文件并解析出其中像素的值,最终计算出了文件的校验和。
总结
本文介绍了 ES10 中的 ArrayBuffer 对象和各种 TypedArray 类型的使用方法,并结合一个实际案例,展示了它们在实际项目中的运用技巧。使用 ArrayBuffer 和 TypedArray,我们可以很方便地读写、解析和计算各种二进制数据,从而获得更丰富的数据存储和处理能力,为前端开发提供了更广阔的应用场景。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6533d3cb7d4982a6eb7751a1