js 几种常见 for 遍历的区别
1 js 几种 for 循环的区别
测试 loop 类型:
for
forEach
forOf
forin
forof
Object.keys
测试维度:
1. 是否可访问非索引的属性
2. 是否会访问到empty元素
3. 是否可中断循环
4. 是否可访问原型属性
5. 是否可访问不可枚举属性
6. 是否支持正常异步
测试用例
const arr = ['a', 'b', 'c']
arr.props = 'test arr'
arr.__proto__.protoKey = 'proto key'
Object.defineProperty(arr, 'hiddenKey', {
value: 'hidden key',
writable: false,
configurable: false,
enumerable: false
})
2 for
ESMAScript1 就存在,兼容性较强。但语法冗长,不优雅
- 是否可访问非索引的属性 否
- 是否可访问 empty 元素 是,打印输出 undefined
- 是否可访问原型链属性 不可
- 是否可访问不可枚举属性 不可
- 是否可中断循环 是,continue 中断本次,break 中断本趟,return 中断函数执行
- 是否支持异步 是,await 表现正常
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]) // a b c
}
delete arr[1] // empty
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]) // a undefined c
}
const asyncFn = () => Promise.resolve(1)
;(async () => {
for (let i = 0; i < arr.length; i++) {
console.log('step1')
console.log(await asyncFn())
console.log('step2')
}
})()
/*
step1
1
step2
...
*/
3 forEach
- 是否可访问非索引的属性 否
- 是否可访问 empty 元素 否,不会执行 empty 的回调
- 是否可访问原型链属性 不可
- 是否可访问不可枚举属性 不可
- 是否可中断循环 不可,只能使用 return 返回本次回调
- 是否支持异步 否,await 表现异常
const asyncFn = () => Promise.resolve(1)
arr.forEach(async (v, i) => {
console.log('begin',v)
console.log(await asyncFn())
console.log('after',v)
})
/*
begin a
begin b
begin c
1
after a
1
after b
1
after c
*/
4 forof
- 是否可访问非索引的属性 否
- 是否可访问 empty 元素 是,打印输出 undefined
- 是否可访问原型链属性 不可
- 是否可访问不可枚举属性 不可
- 是否可中断循环 是,continue 中断本次,break 中断本趟,return 中断函数执行
- 是否支持异步 是,await 表现正常
for (const v of arr) {
console.log(v) // a b c
}
for (const v of arr) {
if (v === 'b') continue // // break; return;也可
console.log(v) // a c
}
;(async () => {
for (const v of arr) {
console.log('step1')
console.log(await asyncFn())
console.log('step2')
}
/*
step1
1
step2
...
*/
})()
5 forin
可以访问原型链上的可枚举属性,如果不需要此功能,考虑结合 hasOwnProperty 或者改用 Object.keys()
- 是否可访问非索引的属性 是
- 是否可访问 empty 元素 不可
- 是否可访问原型链属性 可
- 是否可访问不可枚举属性 不可
- 是否可中断循环 是,continue 中断本次,break 中断本趟,return 中断函数执行
- 是否支持异步 是,await 表现正常
for (const key in arr) {
console.log(key, arr[key])
/*
0
2
props
protoKey
*/
}
;(async () => {
for (const key in arr) {
console.log('begin')
console.log(await asyncFn())
console.log('after')
}
/*
begin
1
after
...
*/
})()
6 总结
类型 | 是否可访问非索引的属性 | 是否会访问到 empty 元素 | 是否可访问原型链属性 | 是否可访问不可枚举属性 | 是否可中断循环 | 是否支持异步 |
---|---|---|---|---|---|---|
for | 否 | 是,打印输出 undefined | 否 | 否 | 是(continue,break,return) | 是(await 表现正常) |
forEach | 否 | 否,不会执行 empty 元素的回调 | 否 | 否 | 否 | 否,表现异常 |
forof | 否 | 是,打印输出 undefined | 否 | 否 | 是(continue,break,return) | 是(await 表现正常) |
forin | 是 | 是,打印输出 undefined | 是 | 否 | 是(continue,break,return) | 是(await 表现正常) |