数组内存分配
js的数组内存分配不是连续的
js数组相对于数据结构中的数组相比有些特殊,同一个数组可以存储不同类型的元素,并且可以任意重置数组的大小。
在JS中数组存在两种形式,一种是与 C/C++ 等相同的在连续内存中存放数据的快数组,另一种是 HashTable 结构的慢数组,是一种典型的字典形式。在 V8 引擎中,直接创建数组默认的方式是创建快数组,会直接为数组开辟一定大小连续的内存。
v8引擎会自动处理快慢数组,如果达到一定的条件会互相转换
Splice
避免创建稀疏数组
数组变得稀疏时,继续使用快速模式存储数组可能会导致大量内存浪费,因为必须为未定义的索引保留空间。
为了解决这个问题,V8 引擎会在某些情况下将数组转换为慢速模式(Dictionary Elements),在这种模式下,数组元素使用哈希表来存储,这样可以更高效地处理稀疏数组,因为只需要为实际存在的元素分配空间。
哈希表中的每个条目包含一个键(索引)和一个值(元素),而不是为所有可能的索引分配连续的内存空间。
- 显式地跳过某些索引赋值:
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