来自:juejin.im/post/5ea63f3ef265da47b177b4b6 | 责编:乐乐

     

   正文   

几种遍历方法中for执行最快,它没有任何额外的函数调用栈和上下文。但在实际开发中我们要结合语义话、可读性和程序性能,去选择究竟使用哪种方案。下面来看for , foreach , map , for...infor...of五种方法现场battle。

自我介绍

for

我是最早出现的一方遍历语句,在座的各位需称我一声爷爷。我能满足开发人员的绝大多数的需求。

// 遍历数组
let arr = [1,2,3];
for(let i = 0;i < arr.length;i++){console.log(i) // 索引,数组下标console.log(arr[i]) // 数组下标所对应的元素
}// 遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
for(let i = 0, keys=Object.keys(profile); i < keys.length;i++){console.log(keys[i]) // 对象的键值console.log(profile[keys[i]]) // 对象的键对应的值
}// 遍历字符串
let str = "abcdef";
for(let i = 0;i < str.length ;i++){console.log(i) // 索引 字符串的下标console.log(str[i]) // 字符串下标所对应的元素
}// 遍历DOM 节点
let articleParagraphs = document.querySelectorAll('.article > p');
for(let i = 0;i<articleParagraphs.length;i++){articleParagraphs[i].classList.add("paragraph");// 给class名为“article”节点下的 p 标签添加一个名为“paragraph” class属性。
}

forEach

我是ES5版本发布的。按升序为数组中含有效值的每一项执行一次 callback 函数,那些已删除或者未初始化的项将被跳过(例如在稀疏数组上)。我是 for 循环的加强版。

// 遍历数组
let arr = [1,2,3];
arr.forEach(i => console.log(i))// logs 1
// logs 2
// logs 3
// 直接输出了数组的元素//遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
let keys = Object.keys(profile);
keys.forEach(i => {console.log(i) // 对象的键值console.log(profile[i]) // 对象的键对应的值
})

map

我也是ES5版本发布的,我可以创建一个新数组,新数组的结果是原数组中的每个元素都调用一次提供的函数后的返回值。

let arr = [1,2,3,4,5];
let res = arr.map(i => i * i);console.log(res) // logs [1, 4, 9, 16, 25]

for...in枚举

我是ES5版本发布的。以任意顺序遍历一个对象的除Symbol以外的可枚举属性。

// 遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
for(let i in profile){let item = profile[i];console.log(item) // 对象的键值console.log(i) // 对象的键对应的值// 遍历数组
let arr = ['a','b','c'];
for(let i in arr){let item = arr[i];console.log(item) // 数组下标所对应的元素console.log(i) // 索引,数组下标// 遍历字符串
let str = "abcd"
for(let i in str){let item = str[i];console.log(item) // 字符串下标所对应的元素console.log(i) // 索引 字符串的下标
}

for...of迭代

我是ES6版本发布的。在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。

// 迭代数组数组
let arr = ['a','b','c'];
for(let item of arr){console.log(item)
}
// logs 'a'
// logs 'b'
// logs 'c'// 迭代字符串
let str = "abc";
for (let value of str) {console.log(value);
}
// logs 'a'
// logs 'b'
// logs 'c'// 迭代map
let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]
for (let entry of iterable) {console.log(entry);
}
// logs ["a", 1]
// logs ["b", 2]
// logs ["c", 3]// 迭代map获取键值
for (let [key, value] of iterable) {console.log(key)console.log(value);
}// 迭代set
let iterable = new Set([1, 1, 2, 2, 3, 3,4]);
for (let value of iterable) {console.log(value);
}
// logs 1
// logs 2
// logs 3
// logs 4// 迭代 DOM 节点
let articleParagraphs = document.querySelectorAll('.article > p');
for (let paragraph of articleParagraphs) {paragraph.classList.add("paragraph");// 给class名为“article”节点下的 p 标签添加一个名为“paragraph” class属性。
}// 迭代arguments类数组对象
(function() {for (let argument of arguments) {console.log(argument);}
})(1, 2, 3);
// logs:
// 1
// 2
// 3// 迭代类型数组
let typeArr = new Uint8Array([0x00, 0xff]);
for (let value of typeArr) {console.log(value);
}
// logs:
// 0
// 255

经过第一轮的自我介绍和技能展示后,我们了解到:

  • for语句是最原始的循环语句。定义一个变量i(数字类型,表示数组的下标),按照一定的条件,对i进行循环累加。条件通常为循环对象的长度,当超过长度就停止循环。因为对象无法判断长度,所以搭配Object.keys()使用。

  • forEach ES5 提出。自称是for语句的加强版,可以发现它比for语句在写法上简单了很多。但是本质上也是数组的循环。forEach每个数组元素执行一次 callback 函数。也就是调用它的数组,因此,不会改变原数组。返回值是undefine

  • map  ES5 提出。给原数组中的每个元素都按顺序调用一次  callback 函数。生成一个新数组,不修改调用它的原数组本身。返回值是新的数组。

  • for...in  ES5 提出。遍历对象上的可枚举属性,包括原型对象上的属性,且按任意顺序进行遍历,也就是顺序不固定。遍历数组时把数组的下标当作键值,此时的i是个字符串型的。它是为遍历对象属性而构建的,不建议与数组一起使用。

  • for...of ES6 提出。只遍历可迭代对象的数据。

能力甄别

作为一个程序员,仅仅认识他们是远远不够的,在实际开发中鉴别他们各自的优缺点。因地制宜的使用他们,扬长避短。从而提高程序的整体性能才是能力之所在。

关于跳出循环体

在循环中满足一定条件就跳出循环体,或者跳过不符合条件的数据继续循环其它数据。是经常会遇到的需求。常用的语句是break 与 continue

简单的说一下二者的区别,就当复习好了。

  • break语句是跳出当前循环,并执行当前循环之后的语句;

  • continue语句是终止当前循环,并继续执行下一次循环;

注意forEach 与map 是不支持跳出循环体的,其它三种方法均支持。

原理 :查看forEach实现原理,就会理解这个问题。

Array.prototype.forEach(callbackfn [,thisArg]{}

传入的function是这里的回调函数。在回调函数里面使用break肯定是非法的,因为break只能用于跳出循环,回调函数不是循环体。

在回调函数中使用return,只是将结果返回到上级函数,也就是这个for循环中,并没有结束for循环,所以return也是无效的。

map() 同理。

map()链式调用

map() 方法是可以链式调用的,这意味着它可以方便的结合其它方法一起使用。例如:reduce()sort()filter() 等。但是其它方法并不能做到这一点。forEach()的返回值是undefined,所以无法链式调用。

// 将元素乘以本身,再进行求和。
let arr = [1, 2, 3, 4, 5];
let res1 = arr.map(item => item * item).reduce((total, value) => total + value);console.log(res1) // logs 55 undefined"

for...in会遍历出原型对象上的属性

Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = ['a', 'b', 'c'];
arr.foo = 'hello
for (var i in arr) {console.log(i);
}
// logs
// 0
// 1
// 2
// foo
// arrCustom
// objCustom

然而在实际的开发中,我们并不需要原型对象上的属性。这种情况下我们可以使用hasOwnProperty() 方法,它会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。如下:

Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = ['a', 'b', 'c'];
arr.foo = 'hello
for (var i in arr) {if (arr.hasOwnProperty(i)) {console.log(i);}
}
// logs
// 0
// 1
// 2
// foo// 可见数组本身的属性还是无法摆脱。此时建议使用 forEach

对于纯对象的遍历,选择for..in枚举更方便;对于数组遍历,如果不需要知道索引for..of迭代更合适,因为还可以中断;如果需要知道索引,则forEach()更合适;对于其他字符串,类数组,类型数组的迭代,for..of更占上风更胜一筹。但是注意低版本浏览器的是配性。

性能

有兴趣的读者可以找一组数据自行测试,文章就直接给出结果了,并做相应的解释。

for > for-of > forEach > map > for-in
  • for 循环当然是最简单的,因为它没有任何额外的函数调用栈和上下文;

  • for...of只要具有Iterator接口的数据结构,都可以使用它迭代成员。它直接读取的是键值。

  • forEach,因为它其实比我们想象得要复杂一些,它实际上是array.forEach(function(currentValue, index, arr), thisValue)它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,这里可能拖慢性能;

  • map() 最慢,因为它的返回值是一个等长的全新的数组,数组创建和赋值产生的性能开销很大。

  • for...in需要穷举对象的所有属性,包括自定义的添加的属性也能遍历到。且for...in的key是String类型,有转换过程,开销比较大。

总结

在实际开发中我们要结合语义话、可读性和程序性能,去选择究竟使用哪种方案。

如果你需要将数组按照某种规则映射为另一个数组,就应该用 map。

如果你需要进行简单的遍历,用 forEach 或者 for of。

如果你需要对迭代器进行遍历,用 for of。

如果你需要过滤出符合条件的项,用 filterr。

如果你需要先按照规则映射为新数组,再根据条件过滤,那就用一个 map 加一个 filter。

总之,因地制宜,因时而变。千万不要因为过分追求性能,而忽略了语义和可读性。在您的统治之下,他们5个只能是各自发挥长处,谁都别想称霸。

IT技术分享社区

个人博客网站:https://programmerblog.xyz

文章推荐程序员效率:画流程图常用的工具程序员效率:整理常用的在线笔记软件远程办公:常用的远程协助软件,你都知道吗?51单片机程序下载、ISP及串口基础知识硬件:断路器、接触器、继电器基础知识

后端:循环遍历的用法介绍相关推荐

  1. c++ hashset的用法_c++ stl容器set成员函数介绍及set集合插入,遍历等用法举例

    c++ stl集合set介绍 c++ stl集合(Set)是一种包含已排序对象的关联容器.set/multiset会根据待定的排序准则,自动将元素排序.两者不同在于前者不允许元素重复,而后者允许. 1 ...

  2. c++ stl容器vector删除(erase),遍历等基本用法介绍及头文件

    Vectors 包含着一系列连续存储的元素,其行为和数组类似.访问Vector中的任意元素或从末尾添加元素都可以在常量级时间复杂度内完成,而查找特定值的元素所处的位置或是在Vector中插入元素则是线 ...

  3. 遍历循环的高级用法(好简单...)

    1.遍历循环 for 2.无限循环while 3.循环控制保留字break continue 4.循环的高级用法else 1.遍历循环 从遍历结构中逐一提取元素,放在循环变量中 由保留字 for 和 ...

  4. python列表用法详解(查找、添加、删除、修改、复制、循环遍历、列表嵌套)

    1. 列表的格式 [数据1, 数据2, 数据3, 数据4......]         列表可以⼀次性存储多个数据,且可以为不同数据类型. 2. 列表的常⽤操作         列表的作⽤是⼀次性存储 ...

  5. 【Linux】Shell脚本中如何使用“循环”遍历“数组”中的元素(包括MySQL的常用指令介绍)

    一.背景 实习过程中,今天mentor突然让我拉取一下远端园区数据库中的部分信息,因为包含很多不同园区的数据信息,而且要以园区为单位生成文件来对数据进行存放,因此自然是需要使用shell脚本来自动生成 ...

  6. foreach循环遍历数组方法vue介绍

    在vue中用foreach循环遍历数组全部元素,利用回调函数对数组进行操作,自动遍历整个数组,且无法break中途跳出循环,不可控,不支持return操作输出,return只用于控制循环是否跳出当前循 ...

  7. python画兔子代码_【后端开发】如何用Python画一只兔子——turtle库circle()画圆函数的详细用法介绍...

    周末学习了一下turtle库的基本函数,试着画了一只大耳朵小兔子,灵感来源是jellycat邦尼兔.turtle库中circle()函数用来画弧,但和通常先确定原点,再根据半径.夹角画弧的方法有所不同 ...

  8. JS拼接字符串的过程中将JSON对象存到某个标签的属性中,encodeURIComponent(),btoa()用法介绍

    JS拼接字符串的过程中将JSON对象存到某个标签的属性中 JS拼接字符串的过程中将JSON对象存到某个标签的属性中,encodeURIComponent(),btoa()用法介绍 案例描述 实现方法 ...

  9. python循环展示大写字母_python调用大写函数python中字典的循环遍历的两种方式

    开发中经常会用到对于字典.列表等数据的循环遍历,但是python中对于字典的遍历对于很多初学者来讲非常陌生,今天就来讲一下python中字典的循环遍历的两种方式. 注意: python2和python ...

最新文章

  1. 在Windows/Ubuntu上使用Visual Studio Code作为Go语言编辑器操作步骤
  2. android发送json格式,Android---创建Json格式数据
  3. 漫谈linux文件IO
  4. 编程之美----不要被阶乘吓到
  5. Ansible Inventory指北进阶
  6. 天猫上线“商家售后服务评价”功能,消费者体验将纳入商家考核指标
  7. rabbitmq实战_RabbitMQ 实战系列之:消息传递
  8. html5 筛子,html5摇骰子游戏
  9. 过去式加ed的发音_【思语小课堂】时态二三事:规则动词过去式的发音规则
  10. Spark精华问答 | spark的组件构成有哪些?
  11. paip.java桌面开发应用与WEB RIA应用
  12. Linux驱动学习--ALSA框架(二)声卡的创建--以SCO虚拟声卡为例
  13. ftp工具绿色版,5款ftp工具绿色版好用推荐
  14. 802.1x准入控制技术
  15. 笔记本计算机被限制无法上网,笔记本无线wifi连接受限制的解决办法
  16. miui11稳定版获取完整root_怎么获取root权限-MIUI11系统开启系统ROOT权限图文教程-支持小米红米全部机型...
  17. 华为rh5885服务器oid_华为RH5885HV3服务器,故障面板指示灯介绍
  18. 迅雷极速版服务器响应,如何阻止迅雷极速版强制更新?这个方法很简单
  19. Java 与 区块链技术_java区块链技术有哪些主要的特点和应用
  20. SQL Server 2008 R2 企业版/开发版/标准版

热门文章

  1. 现代制造工程02:第一部分——刀具(含2个易考点)
  2. html5 hr代码缩减比例,HTML HR size用法及代码示例
  3. 使用extern C改善显式调用dll
  4. div悬浮在固定位置_悬浮式超声波致动器概要及研究动向
  5. 思科网络基础之访问控制列表
  6. shell编辑crontab任务
  7. [补档]暑假集训D5总结
  8. castle windsor学习----- Services and Components 两者的定义
  9. 第五章 Response(JavaTM Servlet 规范3.1 )
  10. Ruby on Rails Tutorial 第六章 用户模型