前言
在编程中,循环是必不可少的方法,它可以让我们重复执行特定的次数来达到实现业务逻辑的目的,那么问题来了,JavaScript中那么多循环方法,应该什么场景用什么方法呢?这是一个值的我们深入研究的话题,也是我要写这篇文章的起因,希望这篇文章能给你在循环的选择上带来一些帮助,内容是我翻阅各种资料总结而来,如有遗漏、错误等问题,请大家及时指正。
JavaScript有哪些循环
名称 | 作用 |
---|---|
for | 对一段代码块重复执行特定次数 |
while | 条件为真时一直重复执行循环体内的代码 |
do-while | 先执行循环体内的代码,再判断条件是否为真,为真时继续循环,否则停止循环 |
for-in | 遍历对象的属性 |
for-of | 用于遍历可迭代对象,如数组、字符串 |
forEach | 遍历数组或类数组,每次循环会调用配置好的函数 |
map | 映射数组中的每个元素并返回一个新的数组 |
filter | 过滤数组中满足条件的元素,然后把这些元素返回到一个新数组中 |
some | 检查数组中是否存在至少一个满足条件的元素,返回的是布尔值 |
every | 检查数组中是否所有元素都满足条件,返回的是布尔值 |
reduce | 用于对数组进行相加、相乘等计算,最终返回计算结果 |
reduceRight | 从数组末尾开始向前进行遍历 |
function | 模拟循环 |
for循环
for循环基本语法
for (let i = 0; i < 5; i++) {
// 循环体
console.log(i); // [0, 1, 2, 3, 4]
}
for循环有三个参数,分别是初始化表达式、条件表达式、迭代表达式
,这三个参数都是非必填。
- 初始化表达式:初始化一个变量,用来迭代
- 条件表达式:如若条件为真,就继续执行循环操作
- 迭代表达式:迭代初始化变量
for循环的原理分析
- 开始循环,执行第一个参数
初始化表达式
,确定本次循环的起点 - 执行第二个参数
条件表达式
,如若条件为false,就会终止循环,如果条件为true,就执行循环体内的代码 - 循环体执行完成后,执行第三个参数
迭代表达式
,在这一步通常用来更新迭代初始化时的那个变量 - 以此规则循环,直到条件表达式为false,循环结束
for循环的使用场景及案例
循环数组
// for循环过程中是可以随时终止的,可以配合if判断,使用return终止循环
let arr = [1, 3, 4, 8];
for (let i = 0; i < arr; i++) {
console.log(arr[i]) // 1 3 4 8
}
终止循环
// for循环过程中是可以随时终止的,可以配合if判断,使用return终止循环
for (let i = 0; i < 5; i++) {
if (i === 3) {
return
}
}
初始化表达式生成多个变量
for (let i = 0, k = 5; i < 5; i++, k--) {
console.log(i, k)
}
输出:
0 5
1 4
2 3
3 2
4 1
for循环的注意事项
- for循环三个参数非必填,如都不传就是死循环,因为第二个参数条件表达式默认值是true。
- 条件表达式决定循环次数,需要注意条件的正确性,以免写成死循环或不循环。
- 如果使用
var
初始化变量,那么变量就是和for循环同一级,可以在循环体外进行访问,如果使用let
初始化变量,该变量就变成了循环体内的私有变量,在循环体外是访问不到的,而且每循环一次,上次的变量都会被销毁,在新的循环之前重新初始化变量,例子如下:
function test(params) {
for (var i = 0; i < 5; i++) {}
console.log(i)
}
test() // 输出5
function test(params) {
for (let i = 0; i < 5; i++) {}
console.log(i)
}
test() // 报错:i is not defined
while
while循环基本语法
let i = 0;
while(i < 3) {
// 循环体
i++
}
while循环的原理分析
while只有一个参数,这个参数要求返回值必须是布尔类型,当参数为true的时候,会一直重复执行循环体内的代码,只有参数变为false,循环才会结束。
while循环的使用场景及案例
终止循环
let i = 0;
while(true) {
i++
console.log(i) // 1 2 3
if (i === 3) {
break // 使用break终止循环
}
}
do-while
do-while循环基本语法
let i = 0;
do {
// 循环体
i++
} while(i < 3)
do-while循环的原理分析
do-while循环参数规则和while的参数一样,do-while循环不管参数是否为true,都会先执行一遍循环体内的代码,可以理解成想做一件事,不管条件是否成立,都会尝试做一次。
for-in
for-in循环基本语法
// 循环对象
let obj = {
name: "智豪",
age: 25
};
for (key in obj) {
// 循环体
console.log(key); // name age
}
for-in循环的原理分析
for-in是一种可以遍历对象所有可枚举属性的循环结构,它会按照对象内属性的顺序,依次把属性名称赋值给循环变量,并且执行循环体内的代码。这里有一个需要注意的点,就是for_in只能循环可枚举属性
,下面我将举例说明一些特殊场景下for_in的意外情况:
对象的symbol属性
let id = Symbol('id');
let user = {
name: 'name',
[id]: 'id'
};
for (let key in user) {
console.log(user[key]); // name
}
这段代码只输出了"name",是因为symbol类型的属性是不可枚举的,所以for-in循环时会自动将它过滤掉。如果想获取该对象中的所有symbol属性,可以使用Object.getOwnPropertySymbols(user)
。
for-in循环字符串或数组
// 循环字符串
for (index in "我是一个字符串") {
// 循环体
console.log(index); // 0 1 2 3 4 5 6
}
// 循环数组
let arr = ['元素1', '元素1'];
arr.new_property = "新增属性"
for (index in arr) {
// 循环体
console.log(index); // 0 1 'new_property'
}
对于数组和字符串,for-in循环会遍历它们的每个元素,并将元素的下标作为键返回。for-in在循环数组的时候,实际上会遍历数组的原型,这是因为数组是对象的特殊形式,它继承了对象的原型,所以新增到数组中的属性也会被for-in遍历到。
for-in循环的使用场景
- 查询对象中所有可枚举属性
- 检查对象中某些属性是否存在
for-in循环的注意事项
- for-in只能遍历可枚举的属性
- 会遍历到对象从原型继承的属性,需要特别注意
for-of
for-of循环基本语法
for (let item of ['小红', '小明', '小黑']) {
// 循环体
console.log(item); // '小红' '小明' '小黑'
}
for-of只能循环可迭代(遍历)对象,比如数组、字符串、map、set等,与for-in循环不同,for-of循环直接遍历对象的元素,而不是属性名,在使用for-of循环时,要注意可迭代对象必须具备迭代器,否则无法进行遍历。
for-of循环注意事项
- 只能用于可迭代对象,确保要遍历的对象是可迭代的,否则会报错
- 无法中断循环:一旦开始循环,只能等到循环自然结束,不能中途退出
- 不能直接用于对象:如前面所说,对象本身不是可迭代对象,需要特殊处理才能用for-of遍历
forEach
forEach循环基本语法
let arr = ['元素1', '元素2', '元素3'];
arr.forEach((item, index, arr) => {
console.log(item) // '元素1' '元素2' '元素3'
console.log(index) // 0 1 2
console.log(arr) // ['元素1', '元素2', '元素3']
})
forEach循环的原理分析
forEach 循环的语法是array.forEach(callback)
,其中callback
是一个函数,接收三个参数:当前元素、当前元素的索引和数组本身。在执行循环时,依次将每个元素传递给回调函数进行处理,且无法在循环中直接停止
。
map
map循环基本语法
let arr = ['f', 'z', 'h'];
let new_arr = arr.map((item, index, arr) => {
return item.toUpperCase()
})
console.log(new_arr); // ['F', 'Z', 'H']
map循环的原理分析
map 循环的语法和forEach一样,是array.map(callback)
,其中callback
是一个函数,接收三个参数:当前元素、当前元素的索引和数组本身,循环时它会创建一个新的数组,其中每个元素都是原数组元素经过回调函数处理后的结果,map 循环的主要作用是对数组元素进行变换,生成一个新的数组。
filter
filter循环基本语法
let arr = ['f', 'z', 'h'];
let new_arr = arr.filter((item, index, arr) => {
return index === 2
})
console.log(new_arr); // ['h']
filter循环的原理分析
filter 循环用于从数组中筛选出符合条件的元素,filter 循环的语法是array.filter(callback)
,其中callback
是一个函数,接收三个参数:当前元素、当前元素的索引和数组本身,它会遍历数组中的每个元素,将满足回调函数条件的元素保留在新的数组中。
some
some循环基本语法
let arr = [1, 4, 6, 2];
let f = arr.some((item, index, arr) => {
return item === 4
});
console.log(f); // true
some循环的原理分析
some循环用于检查数组中是否存在满足特定条件的元素,语法是array.some(callback)
,其中callback
是一个函数,接收三个参数:当前元素、当前元素的索引和数组本身,它会依次对数组中的元素执行回调函数,只要有一个元素满足条件,就立即返回 true,否则返回 false
。
every
every循环基本语法
let arr = [1, 4, 6, 2];
let f = arr.every((item, index, arr) => {
return item === 4
});
console.log(f); // false
every循环和some唯一的区别就是只有当所有元素都满足条件时,才返回true,否则返回false。
reduce
reduce循环基本语法
let arr = [1, 5, 2, 9, 10];
let num = arr.reduce((accumulator, currentElement, index, arr) => {
return accumulator + currentElement
}, 0)
console.log(num); // 27
reduce循环的语法是array.reduce(callback, initialValue)
,其中callback
是一个函数,接收四个参数:初始值(上一次回调的返回值或提供的初始值)
、当前元素值、当前元素索引和调用reduce的数组。initialValue
是初始化累计的值。
reduce循环的原理分析
reduce循环是一种对数组进行归约操作的方法,它接受一个函数作为累加器,并为数组中的每个元素依次执行该回调函数,在每次迭代中,回调函数的返回值将作为下一次迭代的初始值,如果没有提供初始值,则默认使用数组的第一个元素作为初始值,然后从第二个元素开始计算(示例如下),reduce 循环会遍历数组中的所有元素,包括那些可能被删除或从未被赋值的元素。
// 带有默认值
let arr = [1, 5, 2, 9, 10];
let num = arr.reduce((accumulator, currentElement, index, arr) => {
console.log(accumulator + '------' + accumulator + currentElement)
return accumulator + currentElement
}, 0)
输出结果:
0------01
1------15
6------62
8------89
17------1710
// 不带默认值
let arr = [1, 5, 2, 9, 10];
let num = arr.reduce((accumulator, currentElement, index, arr) => {
console.log(accumulator + '------' + accumulator + currentElement)
return accumulator + currentElement
})
输出结果:
1------15
6------62
8------89
17------1710
reduce循环的使用场景及案例
连接数组元素为一个字符串
let words = ['Hello', 'World', '!'];
let sentence = words.reduce((accumulator, currentValue) => accumulator + currentValue);
console.log(sentence); // HelloWorld!
计算数组中所有偶数的平方和
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let evenSquaresSum = numbers.reduce((accumulator, currentValue) => {
if (currentValue % 2 === 0) {
return accumulator + currentValue * currentValue;
}
return accumulator;
}, 0);
console.log(evenSquaresSum);
reduceRight
reduceRight循环基本语法
let arr = [2, 5, 3, 9, 12];
let num = arr.reduceRight((accumulator, currentValue) => {
return accumulator + currentValue
}, 0)
console.log(num); // 31
reduceRight 循环与 reduce 循环类似,都是对数组进行归约操作的方法。不同的是,reduceRight 循环从数组的末尾开始,向前遍历数组元素,并将每个元素与上一次迭代的结果进行累积计算。
function模拟循环
模拟reduceRight计算数组中所有字符串连接起来
function reduceRight(arr, callback, initialValue) {
for (let i = arr.length - 1; i >= 0; i--) {
initialValue = callback(initialValue, arr[i], i, arr);
}
return initialValue;
}
let words = ['Hello', 'World', '!'];
let sentence = reduceRight(words, (accumulator, currentValue) => accumulator + currentValue, '');
console.log(sentence); // !WorldHello
function模拟循环可操作性和灵活性是非常高的,但它也有缺点,比如可读性差,代码结构变得复杂,还是尽量使用JavaScript提供的循环方法。
写在最后
以上内容就是我了解到JavaScript所有的循环了,整体梳理下来感觉并没有特别复杂特别难懂的,语法都差不多,难的就是你能在合适的时候想到合适的循环方法,解决这个难题就要多实践多回顾,拜拜👋🏻。