资讯专栏INFORMATION COLUMN

一文彻底弄懂 for forEach for-in for-of 的区别

newsning / 3108人阅读

基本语法

下面列出了这几个遍历语法规则:

for (let index = 0; index < array.length; index++) {
    const element = array[index]
    // ...
}

array.forEach(element => {
    // ...
})

for (const key in array) {
    // ...
}

for (const iterator of array) {
    // ...
}
分情况讨论这几种写法的不同 非数字的属性

在 JavaScript 中所有的数组都是对象,这意味着你可以给数组添加字符串属性:

array = ["a", "b", "c"]

array.test = "testing"
console.log(array) // [ "a", "b", "c", test: "testing" ]

如果打印,那么这个 test 也会被打印出来

在浏览器中,使用 console.table(array) 打印这个数组可以看到,这个对象中 test 为 index,testing 为 value;其他数组项的 index 值均为数字

上述提到的几个遍历方法中只有 for-in 循环才能够打印出这个键值对:

for (const key in array) {
    console.log(array[key])
}
实际应用的问题

通常情况下,不建议使用 for-in 来遍历数组,除非你知道这个数组对象中没有这样的属性

数组空项

假设要遍历的数组张这样:array = ["a", , "c"]

// a undefined c
for (let index = 0; index < array.length; index++) {
    const element = array[index]
    console.log(element) // 没有跳过空值
}

// a c
array.forEach(element => {
    console.log(element) // 跳过空值
})

// a c
for (const key in array) {
    console.log(array[key]) // 跳过空值
}

// a undefined c
for (const iterator of array) {
    console.log(iterator) // 没有跳过空值
}

上面几个遍历方法,只有 forEach 和 for-in 遍历会跳过空值,值得注意的是,如果空值明确设置为 undefined 如 ["a", undefined, "c"] 那么所有遍历方法都能够将 undefined 遍历出来

实际应用的问题

在 JSON 中是不支持这样的空值的,如果在 parse 方法调用时传入的 JSON 字符串数据含有空值,会报错:

JSON.parse("["a", , "c"]")
// 所以建议使用 for-of 或 for 循环进行遍历,因为如果

stringify 方法调用时,空值会被转为 null 非空值或 undefined

正确的做法应该是保持 undefined,遍历使用 for-of 或 for 循环

建议使用 for-of

方法 this 指向的上下文

在 forEach 中需要传入一个函数,这个函数的 this 指向因语法形式而变化:

for (let index = 0; index < array.length; index++) {
    const element = array[index]
    console.log(this) // {}
}

array.forEach(function (element) {
    console.log(this) // undefined
})

array.forEach(element => {
    console.log(this) // {}
})

for (const key in array) {
    console.log(this) // {}
}

for (const iterator of array) {
    console.log(this) // {}
}

上述遍历写法,只有 forEach 在传入非箭头函数的时候会出现不一致的情况

建议使用箭头函数

Async/Await

async 异步编程中 forEach 则不会按照预期执行,如下:

// a undefined c
{(async () => {
    for (const iterator of array) {
        const result = await new Promise(res => setTimeout(() => { res(iterator) }, 1000))
        console.log(result)
    }
})()}

// a c
{(async () => {
    for (const key in array) {
        const result = await new Promise(res => setTimeout(() => { res(array[key]) }, 1000))
        console.log(result)
    }
})()}

// a undefined c
{(async () => {
    for (let index = 0; index < array.length; index++) {
        const result = await new Promise(res => setTimeout(() => { res(array[index]) }, 1000))
        console.log(result)
    }
})()}

// 语法错误
{(async () => {
    array.forEach(element => {
        const result = await new Promise(res => setTimeout(() => { res(element) }, 1000))
        console.log(result)
    })
})()}

按照上述写法 forEach 会报错,首先看一下 forEach 的原理:

本质上 forEach 就像一个 for 循环的包装:

Array.prototype.forEach = function (callback) {
  for (let index = 0; index < this.length; index++) {
    callback(this[index], index, this)
  }
}

如果按照上述写法,那么在回调函数内部调用 await 需要这个回调函数本身也是 async 函数,因此改为如下写法:

// 语法错误
{(async () => {
    array.forEach(async element => {
        const result = await new Promise(res => setTimeout(() => { res(element) }, 1000))
        console.log(result)
    })
})()}

按照这样写法,forEach 最后会变成并行执行,而非串行。

因此建议使用 for-of 循环

或者创建一个 forEachAwait 方法:

async function forEachAwait(arr, cb) {
    for (let index = 0; index < array.length; index++) {
        await cb(arr[index], index, arr)
    }
}

// a undefined c
{(async () => {
    forEachAwait(array, async (elem) => {
        const result = await new Promise(res => setTimeout(() => { res(elem) }, 1000))
        console.log(result)
    })
})()}
参考:

For vs forEach() vs for/in vs for/of in JavaScript

欢迎订阅我的公众号:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102212.html

相关文章

  • JS 数组循环遍历方法对比

    摘要:循环方法方法不改变原数组方法会给原数组中的每个元素都按顺序调用一次函数。筛选出过滤出数组中符合条件的项组成新数组代码方法方法为数组中的每个元素执行一次函数,直到它找到一个使返回表示可转换为布尔值的值的元素。 showImg(https://segmentfault.com/img/bV2QTD?w=1600&h=500); 前言 JavaScript 发展至今已经发展出多种数组的循环遍...

    BlackFlagBin 评论0 收藏0
  • Array迭代器

    摘要:如上图所示,本文主要阐述了的四种迭代器,,和的功能和区别动态数组每次遍历开始时都会重新计算一次数组的长度稀疏数组不会跳过无效数组动态数组与一样,会遍历到新增加的元素稀疏数组也不会跳过无效数组特点可以遍历字符串可以遍历类数组对象对象可 showImg(https://segmentfault.com/img/bV4PxL?w=3134&h=932); 如上图所示,本文主要阐述了Array...

    Turbo 评论0 收藏0
  • JS中几种包含for遍历方式

    摘要:区别遍历数组常用调用数组的每个元素,并将元素传递给回调函数这种循环的问题在于无法中途跳出循环,和命令不能奏效和都返回数组,返回的与原数组长度相同只返回符合条件的结果一个值和返回布尔值遍历对象所有的可枚举属性主要是为了遍历对象而设计的,不适 forEach,map,filter区别 forEach 遍历数组常用(调用数组的每个元素,并将元素传递给回调函数) let arr = [1,2...

    jcc 评论0 收藏0
  • JavaScript 数组遍历

    摘要:缺陷是不能使用,但可以用来,适用于链式场景,如,适用于全部元素的遍历,缺陷是不知道迭代器,新特性,大家可以慢慢玩 原文链接 《JavaScript 数组遍历》 参考 For-each over an array in JavaScript?Why is for(var item in list) with arrays considered bad practice in JavaSc...

    zhangke3016 评论0 收藏0
  • 数组常见遍历循环方法、数组循环遍历效率对比

    摘要:场景检查数组中是否含有某个东西和是对立的案例案例是否包含为对象循环遍历效率对比参考参考原生实现等方法其他参考 1 遍历数组的方法 1-1、for / while 最普通的循环 效率最高 兼容ie6tips:for循环更适用于循环的开始和结束已知,循环次数固定的场合;while循环更适合于条件不确定的场合 1-2、for in 兼容ie6,效率最差(效率可看最下面的对比) for in...

    xfee 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<