24个 JavaScript 循环遍历方法,你都知道吗?

CUGGZ 2021-09-15 09:39:32
面试 前端 javascript


今天我们来看点基础知识,看看JavaScript中的那些循环遍历方法: JS遍历方法.png

一、数组遍历方法

1. forEach()

forEach 方法用于调用数组的每个元素,并将元素传递给回调函数。数组中的每个值都会调用回调函数。其语法如下:

array.forEach(function(currentValue, index, arr), thisValue) 复制代码

该方法的第一个参数为回调函数,是必传的,它有三个参数:

  • currentValue:必需。当前元素
  • index:可选。当前元素的索引值。
  • arr:可选。当前元素所属的数组对象
let arr = [1,2,3,4,5]
arr.forEach((item, index, arr) => {
console.log(index+":"+item)
})
复制代码

该方法还可以有第二个参数,用来绑定回调函数内部this变量(前提是回调函数不能是箭头函数,因为箭头函数没有this):

let arr = [1,2,3,4,5]
let arr1 = [9,8,7,6,5]
arr.forEach(function(item, index, arr){
console.log(this[index]) // 9 8 7 6 5
}, arr1)
复制代码

注意:

  • forEach 方法不会改变原数组,也没有返回值;
  • forEach无法使用 break,continue 跳出循环,使用 return 时,效果和在 for 循环中使用 continue 一致;
  • forEach 方法无法遍历对象,仅适用于数组的遍历。

2. map()

map() 方法会返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。该方法按照原始数组元素顺序依次处理元素。其语法如下:

array.map(function(currentValue,index,arr), thisValue) 复制代码

该方法的第一个参数为回调函数,是必传的,它有三个参数:

  • currentValue:必须。当前元素的值;
  • index:可选。当前元素的索引值;
  • arr:可选。当前元素属于的数组对象。
let arr = [1, 2, 3];
arr.map(item => {
return item + 1;
})
// 输出结果: [2, 3, 4]
复制代码

该方法的第二个参数用来绑定参数函数内部的this变量,是可选的:

let arr = ['a', 'b', 'c'];
[1, 2].map(function (e) {
return this[e];
}, arr)
// 输出结果: ['b', 'c']
复制代码

该方法还可以进行链式调用:

let arr = [1, 2, 3];
arr.map(item => item + 1).map(item => item + 1)
// 输出结果: [3, 4, 5]
复制代码

注意:

  • map 方法不会对空数组进行检测;
  • map 方法遍历数组时会返回一个新数组,不会改变原始数组
  • map 方法有返回值,可以return出来,map的回调函数中支持return返回值;
  • map 方法无法遍历对象,仅适用于数组的遍历。

3. for of

for...of 语句创建一个循环来迭代可迭代的对象。在 ES6 中引入的 for...of 循环,以替代 for...inforEach() ,并支持新的迭代协议。其语法如下:

for (variable of iterable) {
statement
}
复制代码

该方法有两个参数:

  • variable:每个迭代的属性值被分配给该变量。
  • iterable:一个具有可枚举属性并且可以迭代的对象。

该方法允许获取对象的键值:

let arr = [
{id:1, value:'hello'},
{id:2, value:'world'},
{id:3, value:'JavaScript'}
]
for (let item in arr) {
console.log(item);
}
// 输出结果:0 1 2 
复制代码

注意:

  • for of 方法只会遍历当前对象的属性,不会遍历其原型链上的属性;
  • for of 方法适用遍历 数组/ 类数组/字符串/map/set 等拥有迭代器对象的集合;
  • for of 方法不支持遍历普通对象,因为其没有迭代器对象。如果想要遍历一个对象的属性,可以用 for in 方法;
  • 可以使用break、continue、return来中断循环遍历;

4. filter()

filter()方法用于过滤数组,满足条件的元素会被返回。它的参数是一个回调函数,所有数组元素依次执行该函数,返回结果为true的元素会被返回,如果没有符合条件的元素,则返回空数组。其语法如下:

array.filter(function(currentValue,index,arr), thisValue) 复制代码

该方法的第一个参数为回调函数,是必传的,它有三个参数:

  • currentValue:必须。当前元素的值;
  • index:可选。当前元素的索引值;
  • arr:可选。当前元素属于的数组对象。
const arr = [1, 2, 3, 4, 5]
arr.filter(item => item > 2)
// 输出结果:[3, 4, 5]
复制代码

同样,它也有第二个参数,用来绑定参数函数内部的this变量。

可以使用filter()方法来移除数组中的undefined、null、NAN等值:

let arr = [1, undefined, 2, null, 3, false, '', 4, 0]
arr.filter(Boolean)
// 输出结果:[1, 2, 3, 4]
复制代码

注意:

  • filter 方法会返回一个新的数组,不会改变原数组;
  • filter 方法不会对空数组进行检测;
  • filter 方法仅适用于检测数组。

5. some()、every()

some() 方法会对数组中的每一项进行遍历,只要有一个元素符合条件,就返回true,且剩余的元素不会再进行检测,否则就返回false。 ​

every() 方法会对数组中的每一项进行遍历,只有所有元素都符合条件时,才返回true,如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。其语法如下: ​

两者的语法如下:

array.some(function(currentValue,index,arr),thisValue) array.every(function(currentValue,index,arr), thisValue) 复制代码

两个方法的第一个参数为回调函数,是必传的,它有三个参数:

  • currentValue:必须。当前元素的值;
  • index:可选。当前元素的索引值;
  • arr:可选。当前元素属于的数组对象。
let arr = [1, 2, 3, 4, 5]
arr.some(item => item > 4)
// 输出结果: true
let arr = [1, 2, 3, 4, 5]
arr.every(item => item > 0)
// 输出结果: true
复制代码

注意:

  • 两个方法都不会改变原数组,会返回一个布尔值;
  • 两个方法都不会对空数组进行检测;
  • 两个方法都仅适用于检测数组。

6. reduce()、reduceRight()

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。reduce() 可以作为一个高阶函数,用于函数的 compose。其语法如下:

array.reduce(function(total, currentValue, currentIndex, arr), initialValue) 复制代码

reduce 方法会为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,回调函数接受四个参数:

  • total:上一次调用回调返回的值,或者是提供的初始值(initialValue);
  • currentValue:当前被处理的元素;
  • currentIndex:当前元素的索引;
  • arr:当前元素所属的数组对象。

该方法的第二个参数是 initialValue,表示传递给函数的初始值 (作为第一次调用 callback 的第一个参数):

let arr = [1, 2, 3, 4]
let sum = arr.reduce((prev, cur, index, arr) => {
console.log(prev, cur, index);
return prev + cur;
})
console.log(arr, sum);
复制代码

输出结果:

1 2 1
3 3 2
6 4 3
[1, 2, 3, 4] 10
复制代码

再来加一个初始值试试:

let arr = [1, 2, 3, 4]
let sum = arr.reduce((prev, cur, index, arr) => {
console.log(prev, cur, index);
return prev + cur;
}, 5)
console.log(arr, sum);
复制代码

输出结果:

5 1 0
6 2 1
8 3 2
11 4 3
[1, 2, 3, 4] 15
复制代码

由此可以得出结论:如果没有提供初始值initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供了初始值initialValue,从索引0开始执行

reduceRight() 方法和的reduce()用法几乎一致,只是该方法是对数组进行倒序遍历的,而reduce()方法是正序遍历的。

let arr = [1, 2, 3, 4]
let sum = arr.reduceRight((prev, cur, index, arr) => {
console.log(prev, cur, index);
return prev + cur;
}, 5)
console.log(arr, sum);
复制代码

输出结果:

5 4 3
9 3 2
12 2 1
14 1 0
[1, 2, 3, 4] 15
复制代码

注意:

  • 两个方法都不会改变原数组;
  • 两个方法如果添加初始值,就会改变原数组,会将这个初始值放在数组的最后一位;
  • 两个方法对于空数组是不会执行回调函数的。

7. find()、findIndex()

find() 方法返回通过函数内判断的数组的第一个元素的值。当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。如果没有符合条件的元素返回 undefined。 ​

findIndex() 方法返回传入一个测试函数符合条件的数组第一个元素位置(索引)。当数组中的元素在函数条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,之后的值不会再调用执行函数。如果没有符合条件的元素返回 -1。 ​

两个方法的语法如下:

array.find(function(currentValue, index, arr),thisValue) array.findIndex(function(currentValue, index, arr), thisValue) 复制代码

两个方法的第一个参数为回调函数,是必传的,它有三个参数:

  • currentValue:必需。当前元素;
  • index:可选。当前元素的索引;
  • arr:可选。当前元素所属的数组对象。
let arr = [1, 2, 3, 4, 5]
arr.find(item => item > 2)
// 输出结果: 3
let arr = [1, 2, 3, 4, 5]
arr.findIndex(item => item > 2)
// 输出结果: 2
复制代码

find()findIndex()两个方法几乎一样,只是返回结果不同:

  • find():返回的是第一个符合条件的值;
  • findIndex:返回的是第一个返回条件的值的索引值。

注意:

  • 两个方法对于空数组,函数是不会执行的;
  • 两个方法否不会改变原数组。

8. keys()、values()、entries()

三个方法都返回一个数组的迭代对象,对象的内容不太相同:

  • keys() 返回数组的索引值;
  • values() 返回数组的元素;
  • entries() 返回数组的键值对。

三个方法的语法如下:

array.keys()
array.values()
array.entries()
复制代码

这三个方法都没有参数:

let arr = ["Banana", "Orange", "Apple", "Mango"];
const iterator1 = arr.keys();
const iterator2 = arr.values()
const iterator3 = arr.entries()
for (let item of iterator1) {
console.log(item);
}
// 输出结果: 0 1 2 3
for (let item of iterator2) {
console.log(item);
}
// 输出结果: Banana Orange Apple Mango
for (let item of iterator3) {
console.log(item);
}
// 输出结果:[0, 'Banana'] [1, 'Orange'] [2, 'Apple'] [3, 'Mango']
复制代码

总结:

方法 是否改变原数组 特点
forEach() 没有返回值
map() 有返回值,可链式调用
for of for...of遍历具有Iterator迭代器的对象的属性,返回的是数组的元素、对象的属性值,不能遍历普通的obj对象,将异步循环变成同步循环
filter() 过滤数组,返回包含符合条件的元素的数组,可链式调用
every()、some() some()只要有一个是true,便返回true;而every()只要有一个是false,便返回false.
find()、findIndex() find()返回的是第一个符合条件的值;findIndex()返回的是第一个返回条件的值的索引值
reduce()、reduceRight() reduce()对数组正序操作;reduceRight()对数组逆序操作
keys()、values()、entries() keys() 返回数组的索引值;values() 返回数组元素;entries() 返回数组的键值对。

二、对象遍历方法

1. for in

for…in 主要用于循环对象属性。循环中的代码每执行一次,就会对对象的属性进行一次操作。其语法如下:

for (var in object) {
执行的代码块
}
复制代码

其中两个参数:

  • var:必须。指定的变量可以是数组元素,也可以是对象的属性。
  • object:必须。指定迭代的的对象。
var obj = {a: 1, b: 2, c: 3};
for (var i in obj) {
console.log('键名:', i);
console.log('键值:', obj[i]);
}
复制代码

输出结果:

键名: a
键值: 1
键名: b
键值: 2
键名: c
键值: 3
复制代码

注意:

  • for in 方法不仅会遍历当前的对象所有的可枚举属性,还会遍历其原型链上的属性。

2. Object.keys()、Object.values()、Object.entries()

这三个方法都用来遍历对象,它会返回一个由给定对象的自身可枚举属性(不含继承的和Symbol属性)组成的数组,数组元素的排列顺序和正常循环遍历该对象时返回的顺序一致,这个三个元素返回的值分别如下:

  • Object.keys():返回包含对象键名的数组;
  • Object.values():返回包含对象键值的数组;
  • Object.entries():返回包含对象键名和键值的数组。
let obj = {
id: 1,
name: 'hello',
age: 18
};
console.log(Object.keys(obj)); // 输出结果: ['id', 'name', 'age']
console.log(Object.values(obj)); // 输出结果: [1, 'hello', 18]
console.log(Object.keys(obj)); // 输出结果: [['id', 1], ['name', 'hello'], ['age', 18]
复制代码

注意

  • Object.keys()方法返回的数组中的值都是字符串,也就是说不是字符串的key值会转化为字符串。
  • 结果数组中的属性值都是对象本身可枚举的属性,不包括继承来的属性。

3. Object.getOwnPropertyNames()

Object.getOwnPropertyNames()方法与Object.keys()类似,也是接受一个对象作为参数,返回一个数组,包含了该对象自身的所有属性名。但它能返回不可枚举的属性。

let a = ['Hello', 'World'];
Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ["0", "1", "length"]
复制代码

这两个方法都可以用来计算对象中属性的个数:

var obj = { 0: "a", 1: "b", 2: "c"};
Object.getOwnPropertyNames(obj) // ["0", "1", "2"]
Object.keys(obj).length // 3
Object.getOwnPropertyNames(obj).length // 3
复制代码

4. Object.getOwnPropertySymbols()

Object.getOwnPropertySymbols() 方法返回对象自身的 Symbol 属性组成的数组,不包括字符串属性:

let obj = {a: 1}
// 给对象添加一个不可枚举的 Symbol 属性
Object.defineProperties(obj, {
[Symbol('baz')]: {
value: 'Symbol baz',
enumerable: false
}
})
// 给对象添加一个可枚举的 Symbol 属性
obj[Symbol('foo')] = 'Symbol foo'
Object.getOwnPropertySymbols(obj).forEach((key) => {
console.log(obj[key])
})
// 输出结果:Symbol baz Symbol foo
复制代码

5. Reflect.ownKeys()

Reflect.ownKeys() 返回一个数组,包含对象自身的所有属性。它和Object.keys()类似,Object.keys()返回属性key,但不包括不可枚举的属性,而Reflect.ownKeys()会返回所有属性key:

var obj = {
a: 1,
b: 2
}
Object.defineProperty(obj, 'method', {
value: function () {
alert("Non enumerable property")
},
enumerable: false
})
console.log(Object.keys(obj))
// ["a", "b"]
console.log(Reflect.ownKeys(obj))
// ["a", "b", "method"]
复制代码

注意:

  • Object.keys() :相当于返回对象属性数组;
  • Reflect.ownKeys() :相当于 Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)

总结:

对象方法 遍历基本属性 遍历原型链 遍历不可枚举属性 遍历Symbol
for in
Object.keys()
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Reflect.ownKeys()

三、其他遍历方法

1. for

for循环是应该是最常见的循环方式了,它由三个表达式组成,分别是声明循环变量、判断循环条件、更新循环变量。这三个表达式用分号分隔。可以使用临时变量将数组的长度缓存起来,避免重复获取数组长度,当数组较大时优化效果会比较明显。

const arr = [1,2,3,4,5]
for(let i = 0, len = arr.length; i < len; i++ ){
console.log(arr[i])
}
复制代码

在执行的时候,会先判断执行条件,再执行。for循环可以用来遍历数组,字符串,类数组,DOM节点等。可以改变原数组。

2. while

while循环中的结束条件可以是各种类型,但是最终都会转为布尔值,转换规则如下。

  • Boolean:true为真,false为假;
  • String:空字符串为假,所有非空字符串为真;
  • Number:0为假,非0数字为真;
  • null/Undefined/NaN:全为假;
  • Object:全为真。
let num = 1;
while (num < 10){
console.log(num);
num ++;
}
复制代码

whilefor一样,都是先判断,再执行。只要指定条件为 true,循环就可以一直执行代码。

3. do / while

该方法会先执行再判断,即使初始条件不成立,do/while循环也至少会执行一次。

let num = 10;
do
{
console.log(num);
num--;
}
while(num >= 0);
console.log(num); //-1
复制代码

不建议使用do / while来遍历数组。

4. for await of

for await...of方法被称为异步迭代器,该方法是主要用来遍历异步对象。它是ES2018中引入的方法。

for await...of 语句会在异步或者同步可迭代对象上创建一个迭代循环,包括 String,Array,类数组,Map, Set和自定义的异步或者同步可迭代对象。这个语句只能在 async function内使用:

function Gen (time) {
return new Promise((resolve,reject) => {
setTimeout(function () {
resolve(time)
},time)
})
}
async function test () {
let arr = [Gen(2000),Gen(100),Gen(3000)]
for await (let item of arr) {
console.log(Date.now(),item)
}
}
test()
复制代码

输出结果: image.png

版权声明
本文为[CUGGZ]所创,转载请带上原文链接,感谢
https://juejin.im/post/7007965963819679751

  1. The first starcoin & move hacksong source code analysis - P (a)
  2. Zhaijia 36 days Salt Fish turn into Tencent, Zookeeper Consistency level analysis,
  3. Traitement de l'interception des champs de demande communs de Spring Cloud, plus de 500 personnes interviewent Ali,
  4. About JavaScript modules
  5. Object oriented programming (2)
  6. Java日期时间API系列42-----一种高效的中文日期格式化和解析方法
  7. Java日期時間API系列42-----一種高效的中文日期格式化和解析方法
  8. 宅家36天鹹魚翻身入職騰訊,Zookeeper一致性級別分析,
  9. Java Date Time API Series 42 - - a efficient Chinese Date Format and Analysis Method
  10. 已成功拿下字节、腾讯、脉脉offer,7年老Java一次操蛋的面试经历,
  11. 小米Java社招面试,每次面试必问的二叉树的设计与编码,
  12. 小米Java校招面试,阿里、百度、美团、携程、蚂蚁面经分享,
  13. 小米Java校招面試,阿裏、百度、美團、攜程、螞蟻面經分享,
  14. Xiaomi Java School Recruitment interview, Ali, baidu, meituan, ctrip, ant Facebook Sharing,
  15. La conception et le codage de l'arbre binaire requis pour chaque entrevue d'embauche de la société Java millet;
  16. A remporté avec succès Byte, Tencent, Pulse offer, 7 ans Java une expérience d'entrevue de baise,
  17. 干货来袭,Java岗面试12家大厂成功跳槽,
  18. 常用Java框架面试题目,现在做Java开发有前途吗?
  19. 常用Java框架面試題目,現在做Java開發有前途嗎?
  20. Les questions d'entrevue couramment utilisées pour le cadre Java sont - elles prometteuses pour le développement Java?
  21. L'arrivée de marchandises sèches, l'entretien d'emploi Java 12 grandes usines ont réussi à changer d'emploi,
  22. Multiple postures for handling container time in k8s environment
  23. Echarts remove left Gap, Blank
  24. Hotspot Weekly | zoom $100 million, docker fees, $38 billion Data bricks
  25. JsonMappingException: No serializer found for class org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory...
  26. Java. Security. Securerandom source code analysis Java. Security. EGD = file: / dev /. / urandom
  27. When using IntelliJ idea, jump directly and quickly from the mapper interface to mapper.xml
  28. When idea writes SQL in mybatis XML, the solution to the problems of table name, field and red reporting
  29. Spring cloud integrates Nacos
  30. 应届毕业生Java笔试题目,2021大厂Java社招最全面试题,
  31. Liver explosion! Take you to understand Hadoop serialization
  32. linux系列之:告诉他,他根本不懂kill
  33. java版gRPC实战之三:服务端流
  34. RabbitMQ核心知识总结!
  35. linux系列之:告诉他,他根本不懂kill
  36. java版gRPC实战之三:服务端流
  37. RabbitMQ核心知识总结!
  38. 10天拿到字节跳动Java岗位offer,学习Java开发的步骤
  39. 10天拿到字节跳动Java岗位offer,Java知识点思维导图
  40. Résumé des connaissances de base de rabbitmq!
  41. 10天拿到字節跳動Java崗比特offer,Java知識點思維導圖
  42. 10 jours pour obtenir un Byte Jump Java post offer, Java Knowledge point Mind Map
  43. 10 jours pour obtenir l'octet Jump Java post offer, apprendre les étapes du développement Java
  44. Java version of gppc Reality Three: server side stream
  45. Linux Series: Dites - lui qu'il ne connaît pas kill du tout
  46. "Data structure and algorithm" of front end -- binary search
  47. 2020-2021京东Java面试真题解析,如何才能通过一线互联网公司面试
  48. 13 SpringBoot整合RocketMQ实现过滤消息-根据SQL表达式过滤消息
  49. 12 SpringBoot整合RocketMQ实现过滤消息-根据TAG方式过滤消息
  50. 11 SpringBoot整合RocketMQ实现事务消息
  51. 11 springboot Consolidated rocketmq Implementation transaction message
  52. 12 springboot Consolidated rocketmq Implements Filtering messages - Filtering messages according to tag method
  53. 13 springboot Consolidated rocketmq Implementation Filtering messages - Filtering messages according to SQL expressions
  54. linux系列之:告诉他,他根本不懂kill
  55. (1)java Spring Cloud+Spring boot企业快速开发架构之微服务是什么?它的优缺点有哪些?
  56. Oracle 检查 DATE 列 RANGE 分区表已有分区的最大日期时间
  57. ConcurrentHashMap--原理
  58. 2020 - 2021 JD Java interview Real question Analysis, How can interview through First - Line Internet Company
  59. Concurrenthashmap - - Principes
  60. Oracle vérifie l'heure de date maximale d'une partition existante dans la colonne date