文章首發:github.com/qiudongwei/…git
運行環境:Chrome JavaScript | V8 8.9.255.20github
let array1 = [1];
let array2 = [1];
// array2[2]=2; // 超出數組長度
console.time('array1');
for (let i = 0; i < 10000000; i++) {
array1.push(i);
}
console.timeEnd('array1');
console.time('array2');
for (let j = 0; j < 10000000; j++) {
array2.push(j);
}
console.timeEnd('array2');
複製代碼
① 測試幾回發現,array1和array2耗時相近,幾乎是同樣的; ② 取消第三行的註釋再運行,測試發現,array1的耗時明顯要小於array2;數組
或者直接在這測試:jsben緩存
這是爲何呢?V8內部是如何處理數組操做的?性能優化
在JavaScript層面,開發者能夠給數組的元素設置任意基礎數據類型,eg:arr = [1, 1.2, 'hello', {}, []]
,然而,在V8層面,它理解的元素類型卻只有三種:markdown
舉個例子,暫時忽略PACKED_
這個前綴:app
const arr = [1, 2, 3]; // 元素類型: PACKED_SMI_ELEMENTS
arr.push(1.54); // 元素類型變爲: PACKED_DOUBLE_ELEMENTS
arr.push('c'); // 元素類型變爲: PACKED_ELEMENTS
複製代碼
由上例子可知,V8爲每一個數組分配一種元素類型 SMI_ELEMENTS | DOUBLE_ELEMENTS | ELEMENTS
,而且元素類型的轉換隻能從特定的(eg:SMI_ELEMENTS )到更通常的(eg:ELEMENTS)。函數
再看一個例子:oop
const arr = [1, 2, ,4]; // 元素類型HOLEY_SMI_ELEMENTS
arr.push(5); // 元素類型變爲: HOLEY_DOUBLE_ELEMENTS
arr.push('a'); // 元素類型變爲: HOLEY_ELEMENTS
複製代碼
這個例子裏面,arr出現了個洞(沒有被完滿填充)咱們稱其爲稀疏數組,用一個HOLEY
去標識其類型; PACKED
則表示這是個密集數組。 稀疏數組是不可逆的,一旦標記爲有空,它就是永遠有洞,即使它被填充了!稀疏數組不可轉變爲密集數組,反之卻能夠。以下圖代表了各元素類型之間的轉換關係: 性能
而數組的稀疏與否,有可能(數據量足夠大的時候)會影響數組操做的性能。
new Array
建立數組// bad
const arr = new array(5);
// good
const arr = [0, 0, 0, 0, 0];
// better
const arr = Array.from({length: 5}
複製代碼
const arr = [1, 2, 3];
// bad
log(arr[3]);
複製代碼
length
賦值const arr = [1, 2, 3];
// bad
arr.length = 5;
複製代碼
若是您的代碼須要處理包含多種不一樣元素類型的數組,則可能會比單個元素類型數組要慢,由於你的代碼要對不一樣類型的數組元素進行多態操做。看這個例子:
const each = (array, callback) => {
for (let index = 0; index < array.length; ++index) {
const item = array[index];
callback(item);
}
};
// 第一次調用
each(['a', 'b', 'c'], doSomething);
// 第二次調用
each([1.1, 2.2, 3.3], doSomething);
// 第三次調用
each([1, 2, 3], doSomething);
複製代碼
在第一次調用中,數組類型是 PACKED_ELEMENTS
,V8使用 inline cache
(內嵌緩存)記住 each
函數是被一個特定元素類型調用的,在下一次調用時,V8會先檢查數組類型是不是 PACKED_ELEMENTS
,若是是,則複用上次生成的代碼,不然會作更多的事情,如從新生成相應的代碼;
第二次調用的時候,數組類型是 PACKED_DOUBLE_ELEMENTS
,V8檢查到出現了與緩存中不同的數組類型,此時,V8將 each
函數中的數組標記爲多態,接下來的每一次 each
調用都須要進行 PACKED_ELEMENTS
和 PACKED_DOUBLE_ELEMENTS
兩種類型的檢查,這會形成一些性能消耗;
第三次調用結束的時候,V8緩存中出現了3種不一樣類型的 each
緩存,V8爲了可以複用生成的代碼,接下來的每一次 each
調用,都會作相應的類型檢查。
然而,內置方法(eg:Array.prototype.forEach
)能夠更有效地處理這種多態性,所以在性能敏感的狀況下應用優先考慮使用它。
參考: