JavaScript 陣列遍歷方法的對比

ClarenceC發表於2017-12-21

原文首發至 fe2x.cc 轉載請標註原作者和附帶原文連線

前言

JavaScript 發展至今已經發展出多種陣列的迴圈遍歷的方法,不同的遍歷方法執行起來那個比較快,不同迴圈方法使用在那些場景,下面將進行比較:

各種陣列遍歷的方法

for 語句

程式碼:

var arr = [1,2,4,6]
for(var i = 0, len = arr.length; i < len; i++){
    console.log(arr[i])
}
複製程式碼

這是標準for迴圈的寫法也是最傳統的語句,字串也支援,定義一個變數i作為索引,以跟蹤訪問的位置,len是陣列的長度,條件就是i不能超過len。

forEach 語句

forEach 方法對陣列的每個元素執行一次提供的CALLBACK函式,forEach是一個陣列方法,可以用來把一個函式套用在一個陣列中的每個元素上,forEach為每個陣列元素執行callback函式只可用於陣列.遍歷一個陣列讓陣列每個元素做一件事情.那些已刪除(使用delete方法等情況)或者未初始化的項將被跳過(但不包括那些值為 undefined 的項)(例如在稀疏陣列上);不像map() 或者reduce() ,它總是返回 undefined值,並且不可鏈式呼叫。典型用例是在一個鏈的最後執行副作用。

程式碼:

var arr = [1,5,8,9]
arr.forEach(function(item) {
    console.log(item);
})
複製程式碼

for-in 語句

一般會使用for-in來遍歷物件的屬性的,不過屬性需要 enumerable,才能被讀取到. for-in 迴圈只遍歷可列舉屬性。一般常用來遍歷物件,包括非整數型別的名稱和繼承的那些原型鏈上面的屬性也能被遍歷。像 Array和 Object使用內建建構函式所建立的物件都會繼承自Object.prototype和String.prototype的不可列舉屬性就不能遍歷了.

程式碼:

var obj = {
    name: 'test',
    color: 'red',
    day: 'sunday',
    number: 5
}
for (var key in obj) {
    console.log(obj[key])
}
複製程式碼

for-of 語句 (ES 6)

for-of語句在可迭代物件(包括 Array,Map,Set,String,TypedArray,arguments 物件等等)上建立一個迭代迴圈,呼叫自定義迭代鉤子,併為每個不同屬性的值執行語句。只要是一個iterable的物件,就可以通過for-of來迭代.

程式碼:

var arr = [{name:'bb'},5,'test']
for (item of arr) {
    console.log(item)
}
複製程式碼

for-offor-in 的區別

for-in 語句以原始插入順序迭代物件的可列舉屬性。for-in會把繼承鏈的物件屬性都會遍歷一遍,所以會更花時間.

for-of 語句只遍歷可迭代物件的資料。

Other 迴圈方法

map 方法 (不改變原陣列)

map 方法會給原陣列中的每個元素都按順序呼叫一次 callback 函式。callback 每次執行後的返回值(包括 undefined)組合起來形成一個新陣列。 callback 函式只會在有值的索引上被呼叫;那些從來沒被賦過值或者使用 delete 刪除的索引則不會被呼叫。讓陣列通過某種計算產生一個新陣列,影射成一個新的陣列,

程式碼:

var arr = [1,2,3]
var firearr = arr.map(current => current * 5)
複製程式碼

reduce 方法

讓陣列中的前項和後項做某種計算,並累計最終值,

程式碼:

var wallets = [4,7.8,3]
var totalMoney = wallets.reduce( function (countedMoney, wallet) {
    return countedMoney + wallet.money;
}, 0)
複製程式碼

filter 方法 (不改變原陣列)

filter 為陣列中的每個元素呼叫一次 callback 函式,並利用所有使得 callback 返回 true 或 等價於 true 的值 的元素建立一個新陣列。callback 只會在已經賦值的索引上被呼叫,對於那些已經被刪除或者從未被賦值的索引不會被呼叫。那些沒有通過 callback 測試的元素會被跳過,不會被包含在新陣列中。篩選出過濾出陣列中符合條件的項,組成新陣列.

程式碼:

var arr = [2,3,4,5,6]
var morearr = arr.filter(function (number) {
    return number > 3
})
複製程式碼

every 方法

every 方法為陣列中的每個元素執行一次 callback 函式,直到它找到一個使 callback 返回 false(表示可轉換為布林值 false 的值)的元素。如果發現了一個這樣的元素,every 方法將會立即返回 false。否則,callback 為每一個元素返回 true,every 就會返回 true。檢測陣列中的每一項是否符合條件,如果每一項都符合條件,就會返回true,否則返回false,有點像遍歷陣列且操作callback。只會為那些已經被賦值的索引呼叫。不會為那些被刪除或從來沒被賦值的索引呼叫。

程式碼:

var arr = [1,2,3,4,5]
var result = arr.every(function (item, index) {
    return item > 0
})
複製程式碼

some 方法

some 為陣列中的每一個元素執行一次 callback 函式,直到找到一個使得 callback 返回一個“真值”(即可轉換為布林值 true 的值)。如果找到了這樣一個值,some 將會立即返回 true。否則,some 返回 false。callback 只會在那些”有值“的索引上被呼叫,不會在那些被刪除或從來未被賦值的索引上呼叫。檢查陣列中是否有某些項符號條件,如果有一項就返回true,否則返回false,有點像遍歷陣列或者操作.

程式碼:

var arr = [1,2,3,4,5]
var result = arr.some(function (item,index) {
    return item > 3
})
複製程式碼

對比遍歷速度

對比這裡我使用了jsPerf平臺進行測試.

JavaScritp loop 對比

我建立了兩個陣列進行對比,為什麼要這樣區別呢,因為不同型別的陣列在javascript記憶體中儲存的地址格式不一樣,遍歷的時候編輯器會根椐陣列元素的型別長度計算,比如說如果陣列裡面全是Number類的,迴圈起來會比陣列裡麵包含Number,String,Object混合型的會快,所以建立了兩個陣列,一個是全undefined陣列,一個是混合型陣列.

// 一個是空陣列
var nullarr = new Array(10000) // [undefined,undefined,...undefined]

// 另一個帶不同型別的資料的陣列
var dataarr = []
for(var i = 0; i < 10000; i++){
    if (i % 2 ===0) {
        dataarr[i] = i.toString()
    } else {
        dataarr[i = i
    }
}
dataarr // [1,'2',3...,10000]
複製程式碼

測試後發現有點奇怪直接檢索空陣列還是會比資料陣列慢這是為什麼呢奇怪?為了對比迴圈的一致性我只選其中帶資料的陣列dataarr進行測試.

那我們對比一下 for for len forEach for-in for-of map filter 迴圈的速度

image

可以看到 for迴圈的速度是最快的,是最老的迴圈,也是優化得最好的,其次是for-of這個是es6才新增的迴圈非常好用,最慢是for-in我們可以作一下速度排序

for > for-of > forEach > filter > map > for-in

這很明顯處理大量迴圈資料的時候還是要使用古老for迴圈效率最好,但也不是不使用for-in,其實很多時候都要根據實際應該場景的,for-in更多使用在遍歷物件屬性上面,for-in在遍歷的過程中還會遍歷繼承鏈,所以這就是它效率比較慢的原因,比如map 速率不高,不過處理在Es6實現陣列功能上面非常好用方便,輕鬆影射建立新陣列.或者例如使用Iterator屬性也是行的,所以每個迴圈都有合適使用的地方.

everysome 不完全屬於陣列操作方法

everysome 都是判斷條件直接返回整個陣列Boolean型別的方法.every速度會比some快很多.

image

乾貨

一張圖展示JavaScript陣列方法

image

最後

最後不同瀏覽器核心我相信會有些許差別,有興趣的朋友可以去測試一下,有任何問題歡迎給博主留言.

附上測試的 地址

延伸閱讀

  1. 迭代器(iterator) 地址
  2. JS幾種陣列遍歷方式以及效能分析對比 地址
  3. 如何形象地解釋 JavaScript 中 map、foreach、reduce 間的區別? 地址
  4. For-each over an array in JavaScript? 地址
  5. JavaScript for迴圈效能比較 地址

相關文章