js中数组各种遍历方法的区别
平时无论是使用数组还是对象,遍历都是一个很频繁的操作,在js中遍历数组或对象的方法有很多,在敲代码时有时会由于到底应该使用哪个,是不是使用哪个都一样?带着这样的疑惑,翻了一下文档,写了点代码测试一下,写篇博客记录一下。
一些老生常谈的区别
日常中常用的遍历方法大体有以下几种:for、forEach、for in、for of、map。
1、for循环用于循环执行一定次数的代码块,是一个js语句而不是方法,很通用的一种遍历方法,既可以用来循环数组也可以用来循环类数组的数据结构,可以通过break或者return来跳出整个循环。
2、forEach方法,对数组的每个元素执行一次传入的callback方法,返回值恒为undefined,无法中途退出循环,只能通过return来跳出某次循环
3、for in 语句,这个语句主要是用来循环遍历数组下标或者对象的属性key的,可以通过break或者return来跳出整个循环。
4、for of与for in相似,是用来循环遍历数组单项值或者对象属性值的,可以通过break或者return来跳出整个循环
5、map方法,传入一个callback函数,将每次执行的callback函数返回值组成一个新的数组,不会改变原数组的值。
上面的这些我看过许许多多的文章,文章中大体的比较内容也就上面这些,其中不乏一些文章提到这些循环遍历方法的执行速度问题,我个人觉得,速度最快的肯定是最原始的for循环,其他的那些是会稍微慢一些,但是这个并不会影响日常开发中的使用,根据场景选择适合的方法就行没必要纠结那一点性能。
难以发现的区别
上面说的那些数组遍历的方法大体上都能够正常的实现循环,但其实这些循环方法的执行机制是有一定区别的,这个区别体现在在循环体中执行异步操作。语句类的循环方法和数组原型的循环方法执行异步操作的结果是截然不同的。先看一段代码
let a = [1, 2, 3, 4, 5]
async function test () {
for (let i = 0; i < a.length; i++) {
let res = await fn(a[i])
console.log('for',res)
}
a.forEach(async arr=>{
let res = await fn(arr)
console.log('forEach',res)
})
}
function fn(val) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(val)
}, 1000)
})
}
test()
这段代码运行过一遍之后能很清楚地看到forEach和for的区别,for循环执行的结果是每隔一秒打印一个结果,并且for循环体里面的这个await是能够阻塞下面forEach循环执行的,而forEach执行的结果则大不相同,forEach会在一秒后一次性输出所有的结果,并且不会阻塞放在forEach后面代码的执行。
其原因也很好解释,for循环语句相当于将循环体复制多份在代码中执行,并且为每一份循环体代码提供一个变量i作执行参数。循环体中使用await和直接在外层使用await一样会让后面的代码等待await执行完毕再执行,就产生了每秒输出一次的结果;而像forEach这种以callback的形式来执行循环的方式,当其中有异步操作的时候就会有点像Promise.all一样并行(js单线程语言,假并行来的)运行多个异步操作,这也就导致了上面的forEach代码在一秒后一次性输出所有结果。
最后附上测试代码以及所有循环方式的结果:
let a = [1, 2, 3, 4, 5]
async function test() {
/* for 每隔一秒输出一个*/
for (let i = 0; i < a.length; i++) {
let res = await fn(a[i])
debugger
console.log('for',res)
}
/* forEach 一次性输出 */
a.forEach(async arr=>{
let res = await fn(arr,100)
debugger
console.log('forEach',res)
})
/* map 一次性输出*/
// a.map(async arr => {
// let res = await fn(arr)
// console.log(res)
// })
/* for in 一秒输出一个*/
// for (i in a) {
// let res = await fn(i)
// console.log(res)
// }
/* for of 一秒输出一个 */
// for (i of a) {
// let res = await fn(i)
// console.log(res)
// }
}
function fn(val,time = 1000) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(val)
}, time)
})
}
test()