🔑 定型数组(Typed Arrays)是一种高效的数据结构,旨在提升JavaScript与原生库(如进行图形处理的WebGL)之间数据传输的效率,允许JavaScript直接操作内存中的二进制数据,开发者可以创建固定类型的数组,如整数或浮点数数组,这些数组的表现形式更接近于底层硬件,从而使得数据操作更加快速和内存效率更高。
一些特点
-
直接操作内存:
- 定型数组允许JavaScript代码直接读写二进制数据,这一点与C或C++中的数组和指针操作类似,能够提升处理大量数值数据的效率。
- 相比于JavaScript的常规数组,定型数组因为绕过了JavaScript的高级抽象,而直接操作内存,从而实现了更高的性能。
- 定型数组背后的核心是ArrayBuffer对象,它代表了内存中的一块原始二进制数据区域,而定型数组则提供了一种读写这块内存的接口。
-
固定类型:
- JavaScript的常规数组是动态类型的,可以存储任意类型的数据。而定型数组只能存储单一类型的数据,如
Int32Array仅存储32位整数。 - 这种固定类型的特性使得定型数组的行为更接近于Java或C#中的数组,其中数组的元素类型在声明时就已确定。
- JavaScript的常规数组是动态类型的,可以存储任意类型的数据。而定型数组只能存储单一类型的数据,如
-
字节序:
- 定型数组遵循系统的原生字节序(大端或小端)。这一点在进行网络传输或文件I/O操作时尤其重要,因为不同的系统可能有不同的字节序。
- 这与Python的
struct模块类似,后者也允许开发者指定字节序,但在JavaScript中,字节序的处理对于开发者来说是透明的。
-
与WebGL的紧密集成:
- 定型数组与WebGL等Web标准紧密集成,使得在网页中渲染复杂的3D图形成为可能。
- 这种集成让JavaScript在Web图形领域的应用与C++中使用OpenGL或DirectX的方式有了直接的比较基础,尽管后者更为底层和强大,但JavaScript通过定型数组和WebGL提供了一个更为简便和可访问的3D图形解决方案。
以Todo List为例:
如果Todo List项目涉及到性能敏感的数据处理,例如批量操作大量Todo项的状态更新、统计或者加密解密,那么定型数组就可能派上用场。
例子1:批量更新Todo项状态
假设我们有一个包含成千上万个Todo项的列表,每个Todo项有一个状态值,表示该项是待完成(0)还是已完成(1)。如果我们想要批量更新这些Todo项的状态,可以使用Uint8Array来实现,因为它足以表示这两种状态,并且操作起来非常高效。
初始化Todo状态列表
首先初始化一个定型数组来表示所有Todo项的状态:
// 假设有10000个Todo项
const todosCount = 10000;
// 使用Uint8Array初始化所有Todo项的状态为0(待完成)
const todosStatus = new Uint8Array(todosCount);
批量更新状态
接下来定义一个函数来批量更新Todo项的状态。例如,我们想将前5000项标记为已完成(状态设置为1):
function markAsCompleted(startIndex, endIndex) {
for (let i = startIndex; i <= endIndex; i++) {
todosStatus[i] = 1; // 将状态更新为1(已完成)
}
}
// 将前5000个Todo项标记为已完成
markAsCompleted(0, 4999);
这里使用Uint8Array而不是常规的JavaScript数组的好处在于:
- 内存使用更高效:
Uint8Array为每个状态分配恰好1个字节,而常规数组可能会为相同的数据分配更多内存,尤其是当数组元素是数字时。 - 性能更优:对于大量数据,定型数组的读写速度通常快于常规数组,这是因为它避免了JavaScript引擎的某些动态类型检查。
例子2: 使用定型数组存储Todo项的优先级
假设我们有一个Todo List,每个Todo项除了基本的文本描述外,还有一个1到5的优先级评分。我们可以使用Uint8Array来存储这些优先级,因为Uint8Array是用于存储8位无符号整数的定型数组,非常适合存储这种范围在1到5的小整数。
// 假设我们有5个Todo项,这是它们的优先级数组
const priorities = new Uint8Array([5, 3, 4, 1, 2]);
// 增加一个新的Todo项的优先级
const newPriority = 3; // 新Todo项的优先级
const updatedPriorities = new Uint8Array(priorities.length + 1);
updatedPriorities.set(priorities); // 复制现有优先级
updatedPriorities[updatedPriorities.length - 1] = newPriority; // 添加新优先级
console.log(updatedPriorities); // 输出更新后的优先级数组
首先创建了一个Uint8Array来存储初始的优先级。当我们需要添加一个新的Todo项时,我们创建了一个新的Uint8Array来存储更新后的优先级列表,然后使用.set()方法复制现有的优先级,最后添加新的优先级到数组末尾。
例子3: 加密存储Todo项
如果我们想要在客户端安全地存储Todo项,可以使用定型数组结合Web Crypto API来进行加密。
(async () => {
// 假设这是我们需要加密的Todo项文本
const todoText = "Complete the project documentation";
// 将字符串转换为ArrayBuffer
const encoder = new TextEncoder();
const encoded = encoder.encode(todoText);
// 生成密钥(异步操作)
const key = await window.crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256,
},
true, // 是否可导出
["encrypt", "decrypt"] // 用途
);
// 加密Todo文本(异步操作)
const iv = window.crypto.getRandomValues(new Uint8Array(12)); // 初始向量
window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv,
},
key,
encoded
).then(encrypted => {
// `encrypted` 是一个ArrayBuffer,包含了加密后的数据
console.log(new Uint8Array(encrypted)); // 可以转换为Uint8Array来查看或存储
// 这里可以继续进行解密步骤...
});
})();
首先使用TextEncoder将待加密的字符串转换为ArrayBuffer,然后使用Web Crypto API的encrypt方法对数据进行加密。加密函数返回的encrypted也是一个ArrayBuffer,我们可以将其转换为Uint8Array以便于查看或存储。