javascript/ES6数组及对象的遍历方法汇总,性能分析和功能对比
数组和对象的遍历,是编程中经常遇到的,之前一直都是使用for循环,其实还有很多方法,今天花时间整理一下
最常用的了,没什么可说的,通过es6的字符串repeat及split方法,生成一个长度为1千万数组,供我们测试,并记录循环开始和结束时间,以便我们测试循环的性能
let str='a'.repeat(10000000) let arr=str.split('') let starttime=new Date().getTime() for(let i=0;i<arr.length;i++){ } let endtime=new Date().getTime(); console.log('耗时:'+starttime+','+endtime+','+(endtime-starttime))
耗时:9毫秒左右
对上面进行简单的优化,将数组长度初始化时存储起来,避免每次通过.length读取
for(let i= 0,len=arr.length;i<len;i++){ }
耗时:8毫秒左右
网上还有一种通过判断数组值不为null的方式进行循环,说是效率更高,但是实测耗时要比上面的更长,
for(let i= 0;arr[i]!=null;i++){ }
耗时:18毫秒左右
通过数组的forEach,不需要再定义一个变量 i 用来标识进度
forEach支持两个参数:
第一个参数为回调函数,(value,index,list),value是遍历的值,index是遍历的索引,list是遍历的数组
第二个参数是上下文,也就是回调函数中的this,注意此时不能用箭头函数的写法
let arr=['a','b','c'] arr.forEach(value=>console.log(value)) //1个参数是值 arr.forEach((value,index)=>console.log(index+':',value)) //2个参数是值和索引 arr.forEach((value,index,list)=>list[index]='d') //第3个参数是遍历的数组,可以通过这种方式改变要遍历的数组内容
传入上下文
let obj={ test:'123' } let arr=['a','b','c'] arr.forEach(function(value,index,list){ console.log(this.test) //打印3次 ‘123’ },obj)
注意,forEach没有返回值,不改变原数组,除非你通过传入的list参数显式改变数组内容
性能测试:
let str='a'.repeat(10000000) let arr=str.split('') let starttime=new Date().getTime() arr.forEach(function(value,index,list){ }) let endtime=new Date().getTime(); console.log('耗时:'+starttime+','+endtime+','+(endtime-starttime))
耗时:127毫秒左右
可以看出比普通的for循环,耗时更长
Array.map与forEach类似,也是接受2个参数
第一个参数为回调函数,(value,index,list),value是遍历的值,index是遍历的索引,list是遍历的数组
第二个参数是上下文,也就是回调函数中的this,注意此时不能用箭头函数的写法
不同的是Array.map有返回值,返回值是一个数组,每一项为回调函数中return的值
let arr=['a','b','c'] let newarr=arr.map((value,index,list)=>'d') //回调函数return 'd' console.log(arr) //不变["a", "b", "c"] console.log(newarr) // ["d", "d", "d"]
性能测试
let str='a'.repeat(10000000) let arr=str.split('') let starttime=new Date().getTime() let newarr=arr.map((value,index,list)=>{}) let endtime=new Date().getTime(); console.log('耗时:'+starttime+','+endtime+','+(endtime-starttime))
耗时:1589毫秒左右
相比forEach耗时大大增加
通过for in可以获取遍历的索引,如下
let arr=['a','b','c'] for(let key in arr){ console.log(arr[key]) //遍历到的是索引0,1,2 }
性能测试
let str='a'.repeat(10000000) let arr=str.split('') let starttime=new Date().getTime() for(let key in arr){ } let endtime=new Date().getTime(); console.log('耗时:'+starttime+','+endtime+','+(endtime-starttime))
耗时:1773毫秒左右
比foreach性能低很多
for in遍历到的是索引,而for of遍历到的是value
let arr=['a','b','c'] for(let value of arr){ console.log(value) //遍历到的是 'a','b','c' }
性能测试
let str='a'.repeat(10000000) let arr=str.split('') let starttime=new Date().getTime() for(let value of arr){ } let endtime=new Date().getTime(); console.log('耗时:'+starttime+','+endtime+','+(endtime-starttime))
耗时:200毫秒左右
总结:
for循环的效率最高
forEach 和for of可以直接遍历出数值,效率也还不错
for in 和map效率最低,不过map可以返回一个新数组,用于克隆数组比较方便
慎用for in,除非必要
for in虽说在上面遍历数组时,性能差了点,但是人家可以遍历对象啊哈哈,也算是扳回一局
let obj={ name:'珊瑚学院', url:'shanhuxueyuan.com' } for(let key in obj){ console.log(key+':'+obj[key]) //name:珊瑚学院 url:shanhuxueyuan.com }
再复杂一点,看看for in对继承、不可枚举属性和对象的Symbol属性是否能够遍历
let baseobj={ type:'base', price:100 } let obj={ name:'珊瑚学院', url:'shanhuxueyuan.com', [Symbol()]:'symbol' //定义了一个Symbol属性 } Object.setPrototypeOf(obj,baseobj) //通过setPrototypeOf实现继承 Object.defineProperty(obj,'url',{ enumerable:false //设置url属性不可枚举 }) for(let key in obj){ console.log(key+':'+obj[key]) //name:珊瑚学院 //type:base // price:100 }
for in可以遍历自身和继承的对象的可枚举属性,但是不能遍历Symbol属性
Object.keys返回一个数组,数组中为对象的键,我们可以再通过数组的forEach进行遍历
let obj={ name:'珊瑚学院', url:'shanhuxueyuan.com' } Object.keys(obj).forEach(key=>console.log(key+':'+obj[key]))
同样的看看对继承的遍历
let baseobj={ type:'base', price:100 } let obj={ name:'珊瑚学院', url:'shanhuxueyuan.com', [Symbol()]:'symbol' } Object.setPrototypeOf(obj,baseobj) Object.defineProperty(obj,'url',{ enumerable:false //不可枚举 }) Object.keys(obj).forEach(key=>console.log(key+':'+obj[key])) //name:珊瑚学院
Object.keys只能遍历自身的可枚举属性,但是不能遍历Symbol属性,不能遍历继承对象的属性
直接看实例
let baseobj={ type:'base', price:100 } let obj={ name:'珊瑚学院', url:'shanhuxueyuan.com', [Symbol()]:'symbol' } Object.setPrototypeOf(obj,baseobj) Object.defineProperty(obj,'url',{ enumerable:false //不可枚举 }) Object.getOwnPropertyNames(obj).forEach(key=>console.log(key+':'+obj[key])) //name:珊瑚学院 //url:shanhuxueyuan.com
Object.getOwnPropertyNames可以遍历自身的所有属性(不包括Symbol属性,但是包含不可枚举属性),不能遍历继承对象的属性
let baseobj={ type:'base', price:100 } let obj={ name:'珊瑚学院', url:'shanhuxueyuan.com', [Symbol()]:'symbol' } Object.setPrototypeOf(obj,baseobj) Object.defineProperty(obj,'url',{ enumerable:false //不可枚举 }) Reflect.ownKeys(obj).forEach(key=>console.log(obj[key])) //珊瑚学院 //shanhuxueyuan.com //symbol
Reflect.ownKeys可以遍历自身的所有属性,包括不可枚举属性和Symbol属性
点赞(0)