JavaScript数组离散存储

72 阅读3分钟

数组内存分配

js的数组内存分配不是连续的

js数组相对于数据结构中的数组相比有些特殊,同一个数组可以存储不同类型的元素,并且可以任意重置数组的大小。

在JS中数组存在两种形式,一种是与 C/C++ 等相同的在连续内存中存放数据的快数组,另一种是 HashTable 结构的慢数组,是一种典型的字典形式。在 V8 引擎中,直接创建数组默认的方式是创建快数组,会直接为数组开辟一定大小连续的内存。

v8引擎会自动处理快慢数组,如果达到一定的条件会互相转换

Splice

避免创建稀疏数组

数组变得稀疏时,继续使用快速模式存储数组可能会导致大量内存浪费,因为必须为未定义的索引保留空间。

为了解决这个问题,V8 引擎会在某些情况下将数组转换为慢速模式(Dictionary Elements),在这种模式下,数组元素使用哈希表来存储,这样可以更高效地处理稀疏数组,因为只需要为实际存在的元素分配空间。

哈希表中的每个条目包含一个键(索引)和一个值(元素),而不是为所有可能的索引分配连续的内存空间。

  1. 显式地跳过某些索引赋值:
let sparseArray = [];
sparseArray[0] = 'first';
sparseArray[100] = 'hundredth'; // Creates a sparse array with many empty slots

2. 使用 delete 操作符删除数组元素:

let anotherArray = ['first', 'second', 'third'];
delete anotherArray[1]; // anotherArray is now sparse

3. 直接设置数组的长度大于实际元素数量:

let yetAnotherArray = ['first', 'second'];
yetAnotherArray.length = 100; // Length is set to 100, but only 2 elements are defined

当数组转换为慢速模式时,虽然处理稀疏数组更为高效,但对于非稀疏数组的操作通常会比快速模式慢。

性能优化建议

  • 避免稀疏数组:尽量避免显式跳过索引赋值、删除元素或修改 length 属性,这样可以保证数组始终以快数组的方式存储,避免性能下降。

    javascript
    复制代码
    let denseArray = [];
    for (let i = 0; i < 1000; i++) {
        denseArray[i] = i;
    }
    

    这样的代码会创建一个“稠密”的数组,V8 会使用快数组模式,性能表现更好。

  • 使用 splice 操作代替删除元素:如果你需要删除数组中的元素,尽量使用 splice 方法代替 delete,因为 splice 会自动调整数组的长度并保持数组的连续性。

    javascript
    复制代码
    let arr = ['a', 'b', 'c'];
    arr.splice(1, 1); // Removes 'b' and shifts the remaining elements
    
  • 动态数组增长:当需要动态扩展数组时,尽量避免直接设置 length 属性为过大的值,这会导致数组变成慢数组。可以通过 push 等方法逐步增加元素。

    javascript
    复制代码
    let arr = [];
    arr.push('a'); // Avoid setting arr.length directly
    

zhuanlan.zhihu.com/p/371236424