JavaScript——for和for in 的效能比較與for迴圈的優化方案
在JavaScript中,我們遍歷陣列的時候經常需要用到for和for in。今天來比較一下這兩個遍歷方法的效能,並提供優化方案。
1.for 和for in的效能比較
我們都知道,for 和for in的時間複雜度一樣,但是其效能有些許差距。具體有多大差距呢,下面我們來看一段程式碼。
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-type" content="text/html";charset=utf-8> <script type="text/javascript" > "use strict"; /*如果不加"use strict";會報錯:Uncaught SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode*/ let arr = new Array(); for(let i = 0, len = 1000000;i < len; i++){ arr.push(i); } function for_add(my_arr){ let sum = 0; for(let i = 0; i < my_arr.length; i++){ sum += my_arr[i];//將陣列中的數累加。 } } function for_in_add(my_arr){ let sum = 0; for(let key in my_arr){ sum += my_arr[key];//將陣列中的數累加。 } } function timeTest(func,my_arr,str) { var start_time = null; var end_time = null; start_time = new Date().getTime(); func(my_arr); end_time = new Date().getTime(); console.log(str,(end_time - start_time).toString());//控制檯輸出時間差 } timeTest(for_add,arr,'for_add'); timeTest(for_in_add,arr,'for_in_add'); </script> </head> <body> </body> </html>
這段程式碼定義了一個很大的陣列,分別用for 和for in來遍歷該陣列,並在控制檯輸出這結束時間與開始時間差。執行結果如下:
由此我們可以看出,for的效能要比for in 的效能好很多,但這兩種方法從字面上幾乎一樣,那到底是什麼造成了這樣的差距呢?
原因主要有以下幾點:
1.for...in的key是String型別,而非數字,它包含當前屬性的名稱或當前陣列元素的索引,有轉換過程,因此開銷比較大;但是for迴圈的i是Number型別,開銷較小。
2.for-in是用來迭代物件的屬性或陣列的每個元素,它需要窮舉物件的所有屬性,包括自定義的新增的屬性也能遍歷到,而for則是通過number遍歷,因此開銷較小。因此,for-in 大多時候用來遍歷物件,而非遍歷陣列,遍歷陣列建議用for。
優化方案
1.for 迴圈的優化
(方法一)
當我們遍歷陣列時,下面的程式碼是我們常用的方法:
for(let i = 0; i < my_arr.length; i++){
sum += my_arr[i];//將陣列中的數累加。
}
上面的程式碼看似沒有什麼問題,但是卻包含了一個“超級拖延症患者”,即語句“i<my_arr.length”。這個語句的意思是:i<陣列的長度。看起來好像沒錯,我們的確要陣列長度作為判斷條件,但這種寫法,需要在每一次迴圈都計算一次陣列長度,極大地增加了開銷,想要達到同樣的目的,有另一種更好的方法——將陣列的長度先快取起來。
(方法二)
var l=my_arr.length;
for(let i = 0; i < l; i++){
sum += my_arr[i];//將陣列中的數累加。
}
將陣列長度賦予一個變數,將該變數作為for迴圈中的判斷條件,這樣只需要計算一次陣列長度,無需每次都計算,時間開銷自然也就小了很多。
(方法三)
此外,如果不想要引入新的變數,我們可以用- -替代++。
寫法一:
var i ,arry=[]
for(i=arry.length;i--;){
//倒序對arry進行操作
}
寫法二
var i,arry=[],i=arry.length;
while(i--){
//對arry進行操作
}
(方法四)
除了這個方法,我們還可以運用JavaScript中的技巧,將判斷與迴圈放在一起,一邊迴圈一邊賦值。如下:
for(let i = 0,l;l=my_arr[i++] ; ){
sum += my_arr[i];//將陣列中的數累加。
}
上面的這種寫法將變數直接放到for迴圈的條件語句中,獲取當前的值再也不用在迴圈體裡面定義變數存放,可以減少很多麻煩。但是這種寫法,乍一看以為會報錯,因為沒有條件控制語句,不知道什麼時候跳出迴圈。
但是假設陣列長度為10,如果我們執行一下,會發現迴圈的次數也10。這是為什麼呢? 這就和js的的語句規則有關了。
l=my_arr[i++]為true,而在JavaScript中,0,null,undefined,false和空字串都會是false值。因此,在i變為0的時候自動轉換為布林值false,從而進行迴圈結束終止;如果arr[i++]大於了arr.length,則val就是未定義,判斷為undefined,判斷終止,迴圈結束。
因此,我們可以巧妙利用js中的特定技巧,利用0,null,undefined,false和空字串進行判斷,從而簡寫for迴圈。
總結:JS的for迴圈中,效能的優化主要通過減少迴圈體中變數的宣告和外部方法的呼叫來實現。