基本语法

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

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

欢迎订阅我的公众号:

一文彻底弄懂 for forEach for-in for-of 的区别相关推荐

  1. 一文彻底弄懂大端与小端

    一文彻底弄懂大端与小端 1. 端模式起源 端模式(Endian)起源于<格列佛游记>, 书中根据鸡蛋敲开的方式不同将所有人分为2类,从圆头开始敲的人被归为Big Endian,从尖头开始敲 ...

  2. 一文简单弄懂tensorflow_【TensorFlow】一文弄懂CNN中的padding参数

    在深度学习的图像识别领域中,我们经常使用卷积神经网络CNN来对图像进行特征提取,当我们使用TensorFlow搭建自己的CNN时,一般会使用TensorFlow中的卷积函数和池化函数来对图像进行卷积和 ...

  3. ​Cookie 从入门到进阶:一文彻底弄懂其原理以及应用

    大家好,我是若川.持续组织了8个月源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步.同时极力推荐订阅我写的<学习源码整体架构系列& ...

  4. 一文简单弄懂tensorflow_在tensorflow中设置梯度衰减

    我是从keras入门深度学习的,第一个用的demo是keras实现的yolov3,代码很好懂(其实也不是很好懂,第一次也搞了很久才弄懂) 然后是做的车牌识别,用了tiny-yolo来检测车牌位置,当时 ...

  5. 从原理的视角,一文彻底弄懂FPGA的查找表(LUT)、CLB

    我学东西有个特点,喜欢从原理的层面彻底弄懂一个知识点,这几天想弄明白FPGA的查找表,但发现很多博文写的很模糊,看了以后仍然不是很明白.当然,可能是作者自己弄懂了,但没有站在新人的角度来详细的解释.通 ...

  6. 干货:一文彻底弄懂递归如何解题

    前言 递归是算法中一种非常重要的思想,应用也很广,小到阶乘,再在工作中用到的比如统计文件夹大小,大到 Google 的 PageRank 算法都能看到,也是面试官很喜欢的考点 最近看了不少递归的文章, ...

  7. RabbitMq(二)一文彻底弄懂RabbitMq的四种交换机原理及springboot实战应用

    四大交换机工作原理及实战应用 交换机概念 direct 直连交换机 工作模式图解 springboot代码 Fanout扇出交换机 工作模式图解 springboot代码 Topic主题交换机 工作模 ...

  8. 一文彻底弄懂工厂模式(Factory)

    文章已收录我的仓库:Java学习笔记与免费书籍分享 模式类型 工厂模式属于创建者模式,与对象的创建有关,其中工厂方法模式用于类,而抽象工厂模式用于对象.创建型类模式将对象的部分创建工作延迟到子类,由子 ...

  9. 【GIS风暴】一文彻底弄懂数字地形(DEM、DOM、TDOM、DSM)的区别与联系

    在2021自然资源部发布的<实景三维中国建设技术大纲(2021版)>中,空间数据部分包括"数字高程模型(DEM).数字表面模型(DSM).数字正射影像(DOM).真正射影像(TD ...

最新文章

  1. 细聊一下我的3条面试标准
  2. 0基础学python编程难吗-对于0基础的人,直接学Python编程合适吗?
  3. Java 代码细节与优化(一)
  4. unordered_map自定义key
  5. 修改vsftpd的默认根目录/var/ftp/pub到另一个目录
  6. jquery的全选和多选操作
  7. IndexError: invalid index of a 0-dim tensor. Use `tensor.item()` in Python or `tensor.item<T>()` in
  8. 基于mysql和php的分布式事务处理1,基于MySQL和PHP的分布式事务处理
  9. Web开发——PHP vs Java
  10. Okhttp 向服务器发送请求(请求头,表单,post json数据)...
  11. go语言 html 5 gui,仅需简单 5 步,给你的 Golang 程序添加 GUI (使用 Electron )
  12. android camera API1调用camera HAL3流程学习总结
  13. linux系统学习(常用命令)
  14. 苹果手机怎么定位安卓手机_苹果AirPods搭配安卓手机怎么样?那是相当好
  15. 【Spark】(task5)SparkML基础(分类 | 聚类模型)
  16. 用shell脚本写的一个简单的计算器
  17. 基二FFT时间抽取和频域抽取算法
  18. linux floppy 虚拟,Floppylinux
  19. 安卓Bmob后端云的使用(增删改查、上传图片、推送服务等)
  20. Mybatis中模糊查询的各种写法

热门文章

  1. 22端口限制 git_正在搭 git 服务, iptables 允许 22 端口,然而策略并没有生效
  2. android 截屏 分享,Android应用内截图分享的实现记录
  3. 计算机可爱的企鹅教案,《可爱的企鹅》教学设计
  4. 是以微型计算机为中心 配以相应的外围设备,______是以微型计算机为中心,配以相应的外围设备、电源和辅助电路,以及指挥微型计算机工作的系统软件而构成的。...
  5. python爬取去哪网数据_Python爬虫入门:使用Python爬取网络数据
  6. JAVA转smali软件_Java2Smali(Java代码转Smali工具)
  7. activity finish后没有destroy_Activity 基础知识点
  8. 联想a500手机驱动_一块砖也敢刷:联想手机A368T刷了三次才重新进入系统
  9. c语言谢延红主编答案,C语言程序设计课程改革与实践.doc
  10. pythonweb项目源码下载_最新Python WEB开发在线教育项目之谷粒教育 软件源码齐全...