JavaScript中的几种循环方式
- 1. for 循环
- 1.1 使用方式
- 1.2 不支持遍历对象(但不会报错)
- 2. for-of 循环
- 2.1 使用方式
- 2.2 for-of 和 for 循环比较(不允许修改原数组元素)
- 2.2.1 相同点
- 2.2.1.2 都可以遍历数组,但不允许遍历对象
- 2.2.1.2 都支持 break 和 continue 结束整个/单次循环
- 2.2.2 不同点
- 2.2.2.1 for-of 强行遍历对象,会报错
- 2.2.2.2 不允许直接修改数组内容
- 2.2.2.3 允许修改数组元素的属性(如果元素是对象的话)
- 3. for-in 循环(支持并且建议用来遍历对象,而非数组)
- 3.1 使用方式
- 3.1.1 遍历对象
- 3.1.2 遍历数组
- 3.2 for-in 和 for 循环比较
- 3.2.1 相同点
- 3.2.1.1 都可以遍历数组
- 3.2.1.2 都支持 break 和 continue 结束整个/单次循环,但是for-in还支持对对象进行操作
- 3.2.2 不同点
- 3.2.2.1 可以遍历对象
- 3.2.2.1 不建议用来遍历数组,key值是string类型
- 4. forEach 循环(Array.prototype 数组原型上的方法)
- 4.1 使用方式
- 4.2 和 for 循环比较
- 4.2.1 相同点:都可以遍历数组,并且直接通过数组下标改变元素值
- 4.2.2 不同点
- 4.2.2.1 无法直接通过第一个参数修改元素值
- 4.2.2.2 数组的元素是对象的话,可以直接修改对象属性的方式
- 4.2.2.3 不能遍历对象,强行调用会报错
- 4.2..2.4 不能使用 break 和 continue,会导致语法错误
- 5. map 循环(Array.prototype 数组原型上的方法)
- 5.1 使用方式:遍历原数组,通过映射生成新数组(元素数量不变,值变化)
- 5.2 和 forEach 循环进行比较
- 5.2.1 相同点
- 5.2.1.1 都有3个参数,都是(currentValue, index , arr)
- 5.2.1.2 都无法直接通过currentValue修改原数组
- 5.2.1.3 都可以通过 index 配合 arr(或者原数组)直接修改元素值
- 5.2.2 不同点
- 5.2.2.1 map 循环会返回一个新的数组,forEach不会
- 6. filter 循环(Array.prototype 数组原型上的方法)
- 6.1 使用方式:遍历原数组,筛选元素,组成新的数组(元素个数变化,值不变)
- 6.2 和 map 循环进行比较
- 6.2.1 相同点
- 6.2.1.1 都有3个参数,都是(currentValue, index , arr)
- 6.2.2 不同点
- 6.2.2.1 功能侧重不同,filter筛选元素(新数组元素个数变化,值不变),map是映射元素(新数组元素个数不变,值变化)
- 7. some 循环(Array.prototype 数组原型上的方法)
- 7.1 使用方式:遍历数组,只要有一个元素符合条件,返回true,否则返回false
- 7.2 和 forEach、map 、filter 循环进行比较
- 7.2.1 相同点
- 7.2.1.1 都有3个参数,都是(currentValue, index , arr)
- 7.2.1.2 都无法直接通过currentValue修改原数组
- 7.2.1.3 都可以通过 index 配合 arr(或者原数组)直接修改元素值
- 7.2.2 不同点
- 7.2.2.1 功能侧重不同:some循环是寻找数组中是否有元素符合条件
- 8. every 循环(Array.prototype 数组原型上的方法)
- 8.1 使用方式:遍历数组,只有当所有元素都符合条件返回true,否则返回false
- 8.2 和 some 循环比较(大家都懂,就不说了吧)
- 9. reduce 循环(Array.prototype 数组原型上的方法)
- 9.1 使用方式
- 9.1.1 遍历数组,作为累加器使用
- 9.1.2 支持传入初始值(例子:去重函数)
- 9.2 和其他数组原型链循环方式比较
- 9.2.1 相同点
- 9.2.1.1 都有当前元素、当前索引、数组 3个参数
- 9.2.2 不同点
- 9.2.2.1 作为累加器,增加了 total 和 initialValue 两个参数
- 9.2.2.1 可以函数中处理新数组的元素个数和元素值(基于原数组创建一个元素个数和值都有变化的新数组时,非常好用)
- 10. reduceRight 循环(Array.prototype 数组原型上的方法)
- 10.1 使用方式:和reduce功能一样,只是遍历顺序相反
- 11. while 循环
- 11.1 使用方式
- 11.2 和其他循环比较
- 11.2.1 相同点
- 11.2.1.1 都可以遍历数组
- 11.2.2 不同点
- 11.2.2.1 可以自定义遍历条件,更加灵活
- 11.2.2.2 可以随意对数组进行处理
- 11.2.2.3 作为遍历条件的参数大多数情况下会成为全局变量,需要注意命名,避免重名影响其他变量
- 12. do while 循环
- 12.1 使用方式
- 12.2 和 while 循环比较 :无论如何都会执行第一次循环
- 13. 综合上述
- 13.1 遍历数组,for 循环功能最完善
- 13.2 遍历对象,使用 for-in 循环(不建议遍历数组)
- 13.3 遍历对象数组,需要break或者continue时,可使用 for-of 循环
- 13.4 遍历对象数组,不需要break或者continue时,使用 array.forEach 循环
- 13.5 筛选元素,使用array.filter 循环
- 13.6 修改元素生成新数组,使用 array.map 循环
- 13.7 累加数值,或者处理元素个数和内容时,使用 array.reduce 循环
1. for 循环
for循环是数组循环中最简单,也是最常用的循环方式。
1.1 使用方式
// 方式一:for循环
let arr1 = ['red','yellow','blue']
for(let i = 0; i < arr1.length; i++) {console.log(arr1[i])
}
1.2 不支持遍历对象(但不会报错)
注意:for循环对对象不生效,但是如果强行使用for循环对对象进行遍历,也不会报错,因为根本不会进入循环,相当于一段垃圾代码。
2. for-of 循环
2.1 使用方式
可以看到,如果数组循环中只使用到了数值(没使用索引),VSCode会建议我们使用更简单的循环方式for-of
// 方式二:for of 循环
let arr1 = ['red','yellow','blue']
for(const element of arr1) {console.log(element)
}
2.2 for-of 和 for 循环比较(不允许修改原数组元素)
2.2.1 相同点
2.2.1.2 都可以遍历数组,但不允许遍历对象
2.2.1.2 都支持 break 和 continue 结束整个/单次循环
(1)break
let arr1 = ['red','yellow','blue']
for(let element of arr1) {console.log('element', element)if(element === 'yellow') {break}
}
console.log('arr1', arr1)
(2)continue
let arr1 = ['red','yellow','blue']
for(let element of arr1) {console.log('element', element)if(element === 'yellow') {continue}console.log('continue', element)
}
console.log('arr1', arr1)
2.2.2 不同点
2.2.2.1 for-of 强行遍历对象,会报错
for-of强行遍历对象,会报错
let obj1 = {name: 'Sheldon',age: 26,sex: 'male'
}
for(const element of obj1) {console.log(element)
}
2.2.2.2 不允许直接修改数组内容
for(let element of arr1) {element = 'test'console.log('element', element)
}
console.log('arr1', arr1)
可以看到,即使在for-of修改了元素的值,原数组的值仍然不受任何影响。可以理解为,for-of循环的element只是复制了一份数组的元素提供使用。
2.2.2.3 允许修改数组元素的属性(如果元素是对象的话)
let objArr = [{name: 'Sheldon',age: 26,sex: 'male'}, {name: 'Jane',age: 20,sex: 'female'}, {name: 'Mick',age: 24,sex: 'male'},
]
for(let element of objArr) {element.name = 'me'console.log('element', element)
}
console.log('objArr', objArr)
3. for-in 循环(支持并且建议用来遍历对象,而非数组)
3.1 使用方式
3.1.1 遍历对象
let obj1 = {name: 'Sheldon',age: 26,sex: 'male'
}
// 方式三:for-in 循环
for (const key in obj1) {console.log(key, obj1[key])
}
3.1.2 遍历数组
let arr1 = ['red','yellow','blue']
for (const key in arr1) {console.log(key, arr1[key])
}
3.2 for-in 和 for 循环比较
3.2.1 相同点
3.2.1.1 都可以遍历数组
3.2.1.2 都支持 break 和 continue 结束整个/单次循环,但是for-in还支持对对象进行操作
(1)break
let obj1 = {name: 'Sheldon',age: 26,sex: 'male'
}
for (const key in obj1) {console.log(key, obj1[key])if(key === 'age') {break}
}
(2)continue
let obj1 = {name: 'Sheldon',age: 26,sex: 'male'
}
for (const key in obj1) {console.log(key, obj1[key])if(key === 'age') {continue}console.log('continue', key)
}
3.2.2 不同点
3.2.2.1 可以遍历对象
3.2.2.1 不建议用来遍历数组,key值是string类型
虽然for-in也可以遍历数组,但是从遍历的结果可以看出,其实for-in是把数组当成对象去遍历的。
通常谷歌浏览器在控制台输出数字时,是蓝色的。而在输出字符串时,是绿色的。从这一点也能佐证。
for (const key in arr1) {console.log(key, arr1[key], typeof key)
}
因此,不建议遍历数组使用for-in,或者在需要使用key时,包一层Number(key),避免一些特殊判断时报错。
4. forEach 循环(Array.prototype 数组原型上的方法)
4.1 使用方式
let arr1 = ['red','yellow','blue']
// 方式四:forEach 循环
arr1.forEach((val, index, arr) => {console.log(val, index, arr)
});
4.2 和 for 循环比较
4.2.1 相同点:都可以遍历数组,并且直接通过数组下标改变元素值
let objArr = [
{name: 'Sheldon',age: 26,sex: 'male'}, {name: 'Jane',age: 20,sex: 'female'}, {name: 'Mick',age: 24,sex: 'male'},
]
objArr.forEach((item, index, arr) => {objArr[index] = 'Bruce'
});
console.log('objArr', objArr)
4.2.2 不同点
4.2.2.1 无法直接通过第一个参数修改元素值
第一个参数和第三个参数只是复制元素和数组,并非本身。所以直接修改,不会影响数组本身。
let arr1 = ['red','yellow','blue']
arr1.forEach((val, index, arr) => {console.log(val)val = 'green'arr = []
});
console.log('arr1', arr1)
4.2.2.2 数组的元素是对象的话,可以直接修改对象属性的方式
不过如果元素值是对象的话,可以通过修改对象属性的方式修改(因为复制的是元素是该对象的引用,相当于复制了房间号,房间里的东西可以改变,但是房间号仍然不变)。
let objArr = [
{name: 'Sheldon',age: 26,sex: 'male'}, {name: 'Jane',age: 20,sex: 'female'}, {name: 'Mick',age: 24,sex: 'male'},
]
objArr.forEach((item, index, arr) => {item.name = 'Bruce'
});
console.log('objArr', objArr)
4.2.2.3 不能遍历对象,强行调用会报错
let obj1 = {name: 'Sheldon',age: 26,sex: 'male'
}
obj1.forEach((item, index, arr) => {console.log(item, index, arr)
});
4.2…2.4 不能使用 break 和 continue,会导致语法错误
5. map 循环(Array.prototype 数组原型上的方法)
5.1 使用方式:遍历原数组,通过映射生成新数组(元素数量不变,值变化)
let arr1 = ['red','yellow','blue']
arr1.map((item, index, arr) => {console.log(item, index, arr)
})
5.2 和 forEach 循环进行比较
因为都是Array原型链上的方法,所以直接与 forEach 循环方式进行比较更为合适。
5.2.1 相同点
5.2.1.1 都有3个参数,都是(currentValue, index , arr)
5.2.1.2 都无法直接通过currentValue修改原数组
5.2.1.3 都可以通过 index 配合 arr(或者原数组)直接修改元素值
5.2.2 不同点
5.2.2.1 map 循环会返回一个新的数组,forEach不会
let objArr = [{name: 'Sheldon',age: 26,sex: 'male'}, {name: 'Jane',age: 20,sex: 'female'}, {name: 'Mick',age: 24,sex: 'male'},
]
let nameArr = objArr.map(item => {return item.name
})
console.log('nameArr:', nameArr)let ageArr = objArr.map(item => item.age + 1
)
console.log('ageArr:', ageArr)console.log('objArr:', objArr)
虽然使用 .map 方法可以帮助我们基于原数组快速生成一个新的数组,但是仍然要注意,当原数组元素是对象时,仍然会有修改原数组的风险。
let objArr = [{name: 'Sheldon',age: 26,sex: 'male'}, {name: 'Jane',age: 20,sex: 'female'}, {name: 'Mick',age: 24,sex: 'male'},
]
let ageArr = objArr.map(item => item.age = item.age + 1
)
console.log('ageArr:', ageArr)console.log('objArr:', objArr)
建议使用.map方法生成新数组时,不要对原数组元素进行新的赋值操作,
let ageArr = objArr.map(item => item.age + 1
)
等价于
let ageArr = objArr.map(item => {return item.age + 1
})
建议初学者不要使用省略写法,避免理解上的误差。
6. filter 循环(Array.prototype 数组原型上的方法)
6.1 使用方式:遍历原数组,筛选元素,组成新的数组(元素个数变化,值不变)
let objArr = [{name: 'Sheldon',age: 26,sex: 'male'}, {name: 'Jane',age: 20,sex: 'female'}, {name: 'Mick',age: 24,sex: 'male'},
]
// 方式五: filter 循环
let newArr = objArr.filter((item, index, arr) => {return item.age > 20
})
console.log('newArr', newArr)
6.2 和 map 循环进行比较
6.2.1 相同点
6.2.1.1 都有3个参数,都是(currentValue, index , arr)
6.2.2 不同点
6.2.2.1 功能侧重不同,filter筛选元素(新数组元素个数变化,值不变),map是映射元素(新数组元素个数不变,值变化)
(1)filter是在通过条件筛选出原数组的部分元素,复制到新数组中,因此新数组的元素个数小于等于原数组,并且返回的元素值不变。
(2)map是遍历原数组,取每个元素或者元素中的属性,进行或者不进行处理,获得新的元素,之后再一一填充到新数组。因此新数组的元素个数等于原数组,但是返回的元素值通常会发生变化。
7. some 循环(Array.prototype 数组原型上的方法)
7.1 使用方式:遍历数组,只要有一个元素符合条件,返回true,否则返回false
let objArr = [{name: 'Sheldon',age: 26,sex: 'male'}, {name: 'Jane',age: 20,sex: 'female'}, {name: 'Mick',age: 24,sex: 'male'},
]
// 方式七:some 循环
let checkAge = objArr.some((item, index, arr) => {return item.age > 26
})
console.log('checkAge', checkAge)let checkName = objArr.some((item, index, arr) => {return item.name === 'Mick'
})
console.log('checkName', checkName)
7.2 和 forEach、map 、filter 循环进行比较
7.2.1 相同点
7.2.1.1 都有3个参数,都是(currentValue, index , arr)
7.2.1.2 都无法直接通过currentValue修改原数组
7.2.1.3 都可以通过 index 配合 arr(或者原数组)直接修改元素值
7.2.2 不同点
7.2.2.1 功能侧重不同:some循环是寻找数组中是否有元素符合条件
8. every 循环(Array.prototype 数组原型上的方法)
8.1 使用方式:遍历数组,只有当所有元素都符合条件返回true,否则返回false
et objArr = [{name: 'Sheldon',age: 26,sex: 'male'}, {name: 'Jane',age: 20,sex: 'female'}, {name: 'Mick',age: 24,sex: 'male'},
]
// 方式八:every循环
let allAdult = objArr.every((item, index, arr) => {return item.age >= 20
})
console.log('allAdult', allAdult)let allMale = objArr.every((item, index, arr) => {return item.sex === 'male'
})
console.log('allMale', allMale)
8.2 和 some 循环比较(大家都懂,就不说了吧)
9. reduce 循环(Array.prototype 数组原型上的方法)
9.1 使用方式
9.1.1 遍历数组,作为累加器使用
// 方式九:reduce循环
let numArr = [11, 13, 15, 17, 19]
let totalNum = numArr.reduce((total, item, index , arr) => {console.log(total, item, index, arr)return total + item
})
console.log('totalNum', totalNum)
reduce方法一共有4个参数,total:初始值,item:当前元素,index:当前索引,arr:遍历的数组
可以看到,我们第一次累加时,初始值为第一个元素,当前元素和索引是第二个。因此在处理时,如果元素是对象,注意累加时记得处理。
9.1.2 支持传入初始值(例子:去重函数)
array.reduce(function(total, item, index, arr), initialValue)
let repeatArr = [{ id: 1, name: 'Tom'},{ id: 2, name: 'Jack'},{ id: 1, name: 'Tom'},{ id: 3, name: 'Mike'},{ id: 3, name: 'Mike'},{ id: 2, name: 'Jack'},
]function getUniqueArr(array) {let idObj = {}return array.reduce((pre, item, index ,arr) => {if(!idObj[item.id]) {idObj[item.id] = truereturn [...pre, item]} else {return pre}}, [])
}
console.log('uniqueArr', getUniqueArr(repeatArr))
传入初始值 [ ],第一次的 total, item, index
就变成了[], { id: 1, name: 'Tom'}, 0
9.2 和其他数组原型链循环方式比较
9.2.1 相同点
9.2.1.1 都有当前元素、当前索引、数组 3个参数
9.2.2 不同点
9.2.2.1 作为累加器,增加了 total 和 initialValue 两个参数
9.2.2.1 可以函数中处理新数组的元素个数和元素值(基于原数组创建一个元素个数和值都有变化的新数组时,非常好用)
10. reduceRight 循环(Array.prototype 数组原型上的方法)
10.1 使用方式:和reduce功能一样,只是遍历顺序相反
array.reduceRight(function(total, item, index, arr), initialValue)
let arr1 = ['red','yellow','blue', 'green']
let namesStr = arr1.reduceRight((total, item, index, arr) => {console.log(total, item, index)return total + ',' + item
})console.log('namesStr', namesStr)
11. while 循环
11.1 使用方式
let arr1 = ['red','yellow','blue', 'green']
let i = 0
while(i < arr1.length) {console.log(arr1[i])i++
}
11.2 和其他循环比较
11.2.1 相同点
11.2.1.1 都可以遍历数组
11.2.2 不同点
11.2.2.1 可以自定义遍历条件,更加灵活
11.2.2.2 可以随意对数组进行处理
11.2.2.3 作为遍历条件的参数大多数情况下会成为全局变量,需要注意命名,避免重名影响其他变量
12. do while 循环
12.1 使用方式
let i = 0
do {console.log(arr1[i])i++
} while(i < arr1.length)
12.2 和 while 循环比较 :无论如何都会执行第一次循环
13. 综合上述
不同循环方式都有其优势和劣势,我们应该根据具体场景使用。
13.1 遍历数组,for 循环功能最完善
遍历数组,最经典也是最好用的就是 for 循环,功能最全。如果觉得记忆太麻烦,记住这一种遍历方式即可。
13.2 遍历对象,使用 for-in 循环(不建议遍历数组)
遍历对象,使用 for-in 循环,虽然for-in也支持遍历数组,但是更加适合遍历对象;
let obj1 = {name: 'Sheldon',age: 26,sex: 'male'
}
// 方式三:for-in 循环
for (const key in obj1) {console.log(key, obj1[key])
}
13.3 遍历对象数组,需要break或者continue时,可使用 for-of 循环
遍历对象数组,并且当数组数据量过大,需要从循环中break或者continue释放出来时,使用 for-of 循环;
let arr1 = ['red','yellow','blue']
for(let element of arr1) {console.log('element', element)if(element === 'yellow') {break}
}
console.log('arr1', arr1)
13.4 遍历对象数组,不需要break或者continue时,使用 array.forEach 循环
let arr1 = ['red','yellow','blue']
// 方式四:forEach 循环
arr1.forEach((val, index, arr) => {console.log(val, index, arr)
});
13.5 筛选元素,使用array.filter 循环
(5)筛选符合条件的元素生成新数组时,使用 array.filter 循环
let objArr = [{name: 'Sheldon',age: 26,sex: 'male'}, {name: 'Jane',age: 20,sex: 'female'}, {name: 'Mick',age: 24,sex: 'male'},
]
// 方式五: filter 循环
let newArr = objArr.filter((item, index, arr) => {return item.age > 20
})
console.log('newArr', newArr)
## 13.6
13.6 修改元素生成新数组,使用 array.map 循环
基于原数组修改数据生成新数组时,使用 array.map 循环;
let objArr = [{name: 'Sheldon',age: 26,sex: 'male'}, {name: 'Jane',age: 20,sex: 'female'}, {name: 'Mick',age: 24,sex: 'male'},
]
let nameArr = objArr.map(item => {return item.name
})
console.log('nameArr:', nameArr)let ageArr = objArr.map(item => item.age + 1
)
console.log('ageArr:', ageArr)console.log('objArr:', objArr)
13.7 累加数值,或者处理元素个数和内容时,使用 array.reduce 循环
作为累加器或者需要对数组的个数和值进行处理时,使用 array.reduce 循环
// 方式九:reduce循环
let numArr = [11, 13, 15, 17, 19]
let totalNum = numArr.reduce((total, item, index , arr) => {console.log(total, item, index, arr)return total + item
})
console.log('totalNum', totalNum)
array.reduce(function(total, item, index, arr), initialValue)
let repeatArr = [{ id: 1, name: 'Tom'},{ id: 2, name: 'Jack'},{ id: 1, name: 'Tom'},{ id: 3, name: 'Mike'},{ id: 3, name: 'Mike'},{ id: 2, name: 'Jack'},
]function getUniqueArr(array) {let idObj = {}return array.reduce((pre, item, index ,arr) => {if(!idObj[item.id]) {idObj[item.id] = truereturn [...pre, item]} else {return pre}}, [])
}
console.log('uniqueArr', getUniqueArr(repeatArr))