首先来看下面两段代码
const arr1 = [];
for (let i=0; i<100000000; ++i){
arr1[i] = 1;
}
const arr2 = [];
arr2[100000000 - 1] = 1;
for (let i = 0; i<100000000; ++i){
arr2[i] = 1;
}
两个代码都是在初始化一个长度为1亿的数组,区别是第二段代码先为最后一项赋值了一次
执行后发现,两端代码的耗时差了几倍
仅仅是因为片段2先为最后一项赋值了一次,之所以会多出这部分的耗时,归根结底是因为V8会以不同的形式去存储JS的数组,有两种模式:
快速模式:对应C语言的数组,速度快,紧凑。
字典模式:对应C语言的哈希表,速度慢,松散
V8数组模式的触发机制:
快速模式:“索引从0到length-1且无空洞”或“预分配数组小于100000,无论有无空洞”。
字典模式:预分配数组大于等于100000,且数组有空洞。
回到上面第二段代码例子,当我们给数组最后一项赋值的时候,本质上相当于这个数组已经具备了一定的长度而且是大于10万的,并且这个数组中间有着非常多的空洞,只有最后一位有数字。这意味着该操作执行完后,数组就会在V8里面进入了字典模式,就会导致在前面耗时会多出很多。
以下是V8数组模式的优化策略:
1.从0开始连续的初始化数组,以避免数组进入字典模式。
2.不要预分配一个超大数组(比如长度大于等于100000)。
3.删除数组元素时让数组保持紧凑,尽可能避免使用delete。
4.不要访问未初始化或已删除的数组元素。