前言
本文講述了JS常用的幾種數組遍歷方式以及性能分析對比。
如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公衆號首發,關注 前端南玖 第一時間獲取最新的文章~
數組的方法
JavaScript發展到現在已經提供了許多數組的方法,下面這張圖涵蓋了數組大部分的方法,這篇文章主要說一說數組的遍歷方法,以及各自的性能,方法這麼多,如何挑選性能最佳的方法對我們的開發有非常大的幫助。
數組遍歷的方法
for
標準的for循環語句,也是最傳統的循環語句
var arr = [1,2,3,4,5]
for(var i=0;i<arr.length;i++){
console.log(arr[i])
}
最簡單的一種遍歷方式,也是使用頻率最高的,性能較好,但還能優化
優化版for循環語句
var arr = [1,2,3,4,5]
for(var i=0,len=arr.length;i<len;i++){
console.log(arr[i])
}
使用臨時變量,將長度緩存起來,避免重複獲取數組長度,尤其是當數組長度較大時優化效果纔會更加明顯。
這種方法基本上是所有循環遍歷方法中性能最高的一種
forEach
普通forEach
對數組中的每一元素運行給定的函數,沒有返回值,常用來遍歷元素
var arr5 = [10,20,30]
var result5 = arr5.forEach((item,index,arr)=>{
console.log(item)
})
console.log(result5)
/*
10
20
30
undefined 該方法沒有返回值
*/
數組自帶的foreach循環,使用頻率較高,實際上性能比普通for循環弱
原型forEach
由於foreach是Array型自帶的,對於一些非這種類型的,無法直接使用(如NodeList),所以纔有了這個變種,使用這個變種可以讓類似的數組擁有foreach功能。
const nodes = document.querySelectorAll('div')
Array.prototype.forEach.call(nodes,(item,index,arr)=>{
console.log(item)
})
實際性能要比普通foreach弱
for...in
任意順序遍歷一個對象的除Symbol以外的可枚舉屬性,包括繼承的可枚舉屬性。
一般常用來遍歷對象,包括非整數類型的名稱和繼承的那些原型鏈上面的屬性也能被遍歷。像 Array和 Object使用內置構造函數所創建的對象都會繼承自Object.prototype和String.prototype的不可枚舉屬性就不能遍歷了.
var arr = [1,2,3,4,5]
for(var i in arr){
console.log(i,arr[i])
} //這裏的i是對象屬性,也就是數組的下標
/**
0 1
1 2
2 3
3 4
4 5 **/
大部分人都喜歡用這個方法,但它的性能卻不怎麼好
for...of(不能遍歷對象)
在可迭代對象(具有 iterator 接口)(Array,Map,Set,String,arguments)上創建一個迭代循環,調用自定義迭代鉤子,併爲每個不同屬性的值執行語句,不能遍歷對象
let arr=["前端","南玖","ssss"];
for (let item of arr){
console.log(item)
}
//前端 南玖 ssss
//遍歷對象
let person={name:"南玖",age:18,city:"上海"}
for (let item of person){
console.log(item)
}
// 我們發現它是不可以的 我們可以搭配Object.keys使用
for(let item of Object.keys(person)){
console.log(person[item])
}
// 南玖 18 上海
這種方式是es6裏面用到的,性能要好於forin,但仍然比不上普通for循環
map
map: 只能遍歷數組,不能中斷,返回值是修改後的數組。
let arr=[1,2,3];
const res = arr.map(item=>{
return item+1
})
console.log(res) //[2,3,4]
console.log(arr) // [1,2,3]
every
對數組中的每一運行給定的函數,如果該函數對每一項都返回true,則該函數返回true
var arr = [10,30,25,64,18,3,9]
var result = arr.every((item,index,arr)=>{
return item>3
})
console.log(result) //false
some
對數組中的每一運行給定的函數,如果該函數有一項返回true,就返回true,所有項返回false才返回false
var arr2 = [10,20,32,45,36,94,75]
var result2 = arr2.some((item,index,arr)=>{
return item<10
})
console.log(result2) //false
reduce
reduce()
方法對數組中的每個元素執行一個由你提供的reducer函數(升序執行),將其結果彙總爲單個返回值
const array = [1,2,3,4]
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
filter
對數組中的每一運行給定的函數,會返回滿足該函數的項組成的數組
// filter 返回滿足要求的數組項組成的新數組
var arr3 = [3,6,7,12,20,64,35]
var result3 = arr3.filter((item,index,arr)=>{
return item > 3
})
console.log(result3) //[6,7,12,20,64,35]
性能測試
工具測試
使用工具測試性能分析結果如下圖所示
手動測試
我們也可以自己用代碼測試:
//測試函數
function clecTime(fn,fnName){
const start = new Date().getTime()
if(fn) fn()
const end = new Date().getTime()
console.log(`${fnName}執行耗時:${end-start}ms`)
}
function forfn(){
let a = []
for(var i=0;i<arr.length;i++){
// console.log(i)
a.push(arr[i])
}
}
clecTime(forfn, 'for') //for執行耗時:106ms
function forlenfn(){
let a = []
for(var i=0,len=arr.length;i<len;i++){
a.push(arr[i])
}
}
clecTime(forlenfn, 'for len') //for len執行耗時:95ms
function forEachfn(){
let a = []
arr.forEach(item=>{
a.push[item]
})
}
clecTime(forEachfn, 'forEach') //forEach執行耗時:201ms
function forinfn(){
let a = []
for(var i in arr){
a.push(arr[i])
}
}
clecTime(forinfn, 'forin') //forin執行耗時:2584ms (離譜)
function foroffn(){
let a = []
for(var i of arr){
a.push(i)
}
}
clecTime(foroffn, 'forof') //forof執行耗時:221ms
// ...其餘可自行測試
結果分析
經過工具與手動測試發現,結果基本一致,數組遍歷各個方法的速度:傳統的for循環最快,for-in最慢
for-len
>
for>
for-of>
forEach>
map>
for-in
javascript原生遍歷方法的建議用法:
- 用
for
循環遍歷數組 - 用
for...in
遍歷對象 - 用
for...of
遍歷類數組對象(ES6) - 用
Object.keys()
獲取對象屬性名的集合
爲何for… in會慢?
因爲for … in
語法是第一個能夠迭代對象鍵的JavaScript語句,循環對象鍵({})與在數組([])上進行循環不同,引擎會執行一些額外的工作來跟蹤已經迭代的屬性。因此不建議使用for...in
來遍歷數組
推薦閱讀
這些瀏覽器面試題,看看你能回答幾個?
這一次帶你徹底瞭解前端本地存儲
面試官:說一說前端路由與後端路由的區別
JavaScript之原型與原型鏈
Javascript深入之作用域與閉包
this指向與call,apply,bind
覺得文章不錯,可以點個贊呀_ 另外歡迎關注留言交流~