JavaScript 之 循环语句
所谓循环,就是重复 ,重复的执行一段代码,前提是重复要有尽头,不能无穷无尽的无休止重复下去。
循环有4个要素:
1)有一个初始值表达式
2)条件判断表达式
3)要重复执行的代码表达式
4)改变初始化变量(重要,千万不要写死循环)
JavaScript 中的循环语句有哪些?各自的特点又有什么不同呢?……下面,就让我们逐一解开。
常用循环语句
1、for 循环
语法:
for ( 初始值; 条件判断; 改变初始值 ) { 满足条件时重复执行的代码 }
语法示例:
for(var i = 0; i < 10; i++){ console.log(i) };
剖析:
for
循环的代码执行顺序
var i = 0; // 第一步:初始值
i < 10; // 第二步:条件判断
console.log(i); // 第三步:条件满足时执行的代码
i++; // 第四步:改变初始值
... // 一直重复执行 2 3 4 步,直到条件不满足时停止执行。
注意:for()
里面的初始值; 条件判断; 改变初始值
这三个表达式是用分号隔开,不是逗号!分号隔开,不是逗号!分号隔开,不是逗号!
2、循环的控制
控制循环的关键字
1)break
:当循环里面,只要代码执行了 break
,那么就立即结束循环, 跳到循环外面;
示例:
for (var i = 1; i <= 10; i++) {
console.log('我吃的第' + i + '个包子')
if (i === 4) {
// 表示我已经吃了4个包子了, 吃不下了
// 后面的5 6 7 8 9 10 我就不吃了
break
}
}
2)continue
:当循环里面,只要代码执行了 continue
,就立即结束本次循环, 直接进入下一次循环。
示例:
for (var i = 1; i <= 10; i++) {
if(i === 5){
// 这个包子我不吃了, 我直接跳过去吃下一个包子
// continue 后面的代码就不执行了, 直接去到 i++
continue
}
console.log('我吃的第 ' + i + ' 个包子')
}
3、for in 循环
for in
循环是基于 for
循环的基础上的一种特殊的循环。
语法示例:
var arr = [12, 23, 34, 45, 56];
for(var i in arr){
if(i == 0) {
console.log('停止当前循环,continue');
}
if(i == 2) {
console.log('当i = 2时')
break;
// return; // 不能使用return,报错:Illegal return statement(非法的返回语句)
}
console.log('i', i, 'i的类型', typeof i);
console.log(arr[i]); // 打印结果为 12 23 34 45 56
}
如图所示:
for in
循环支持break
结束循环,但是不支持continue
;
剖析:
从语法示例上我们可以看出,for in 循环代码更加简洁,但同时伴随着一定的弊端:
for in 循环和普通 for 循环的区别:
1、在循环数组时,for in 循环给循环变量赋值为字符串类型,而普通 for循环给循环变量赋值为数值类型;
2、在循环对象时,for in
循环可以循环对象,而普通for
循环,不能直接循环对象。其实,for in
循环在循环数组时就是把数组当成了对象来循环,数组的下标就是相当于对象中键值对的键,而浏览器在处理数组时,也可以把数组当成对象,就像盛行的一句行话:万物皆对象。
剖析 for in
循环代码如下:
var arr = [12, 23, 34, 45, 56];
// 数组arr当成对象之后的解析如下
arr={
"0": 12,
"1": 23,
"2": 34,
"3": 45,
"4": 56,
}
var arr = [12, 23, 34, 45, 56];
for(var i in arr){
console.log(arr[i]); // 打印结果为 12 23 34 45 56
console.log(typeof arr[i]); // 打印结果为 number
console.log(i); // 打印循环变量结果为 0 1 2 3 4
console.log(typeof i); // 打印循环变量类型为 string 类型
}
4、for of 循环
注:for of
循环只能循环具有长度含义( length
或者size
)属性的数据类型
语法示例:
- 循环字符串
let str = 'abcd';
console.log(str.length); // 4
for(let i of str) {
if(i == 'b') {
continue;
}
if(i == 'd') {
break;
}
console.log('i', i); // a b c d
}
- 循环数组
let arr = [12, 23, 34, 45, 56];
console.log(arr.length); // 5
for(let i of arr){
if(i == 23) {
continue;
}
if(i == 45) {
break;
}
console.log('i', i, '= i 的类型 = ', typeof i);
}
- 循环伪数组
let s1 = new Set([12, 23, 34, 45, 56]);
console.log(s1.size);
for(let item of s1){
if(item == 23) {
continue;
}
if(item == 45) {
break;
}
console.log('item', item, '= item 的类型 = ', typeof item);
}
注:循环变量为该数据中的每一个下标对应的数据;
- 循环对象
当循环的数据类型没有长度含义(length
或者size
)属性时:
let obj = {
name: 'aaa',
age: 12
}
console.log(obj.length); // undefined
for(let i of obj){
console.log(i); // TypeError: obj is not iterable 报错!!!
}
5、while 循环
语法:
while (判断条件) { 条件满足时重复执行的代码 }
语法示例:
var i = 1; // 第一步:初始值
while(i <= 10){ // 第二步:条件判断
// if(i == 2) {
// continue; // 不能在第四步改变初始值的之前写 continue,不然会陷入死循环
// }
if(i == 6) {
break;
}
console.log('i', i, '= i 的类型 = ', typeof i); // 第三步:条件满足时执行的代码
i++; // 第四步:改变初始值
if(i == 2) {
continue; // 在这里写的continue 没有意义,因为代码执行到这里的时候本就会继续执行下去
}
}
写了continue
的控制台打印:
没写 continue
的控制台打印:
这两者的结果是一样的。
剖析:
while
的中文释义为当…的时候,那么我们就需要在执行 while
语句时先有一个初始值,并给初始值设定一个范围一个条件,条件满足时执行重复的代码,最后再改变初始值。
剖析后,很明显,while
循环和 for
循环剖析后的大同小异,执行顺序都是如此重复执行第 2 3 4 步的代码,直到条件不满足时结束循环。
6、do…while 循环
语法:
do {
重复执行的代码
} while (条件)
do...while
循环与 while
循环的代码执行顺序基本一致,break、continue
的用法也与普通 while
一致,在此便不多做演示;
var i = 1 // 1. 初始化变量
do {
console.log(i) // 3. 重复执行的代码
i++ // 4. 改变自身
} while (i < 10) // 2. 条件判断
只不过写法不一样,且不管条件满不满足都会先执行一遍 do 后面 重复执行的代码,至少执行一遍。
示例 2 ——利用 confirm()
打印语句:
do {
// 把 改变自身 和 执行代码变成一句话了
var res = confirm('你爱不爱我 ? ')
} while (res === false) // 只要用户点击的取消, 就再执行一遍 do
剖析示例 2 的代码:
confirm()
打印语句的返回值是一个布尔值,如果你点击的是确定, 那么就是 true
,如果你点击的是取消, 那么就是 false;
不管条件判断, 先弹出一个选择框,只要用户点击的不是 true
,就再次执行重复执行的代码, 再次弹出一个选择框,直到用户选择了 true
, 就不再执行代码。
7、forEach( ) 循环
功能:循环遍历数组中的每个数据,并对数据进行运算,改变原数组;
参数:回调函数;
返回值:无
语法:forEach( 回调函数 )
;
注意: forEach() 对于空数组是不会执行回调函数的。
示例:
var arr = [5, 12, 18, 19, 20, 90];
var newArr = arr.forEach((item, index, currArr) => {
// if(index == 2) {
// continue;
// // 不能使用 continue,
// // 会报错:Illegal continue statement: no surrounding iteration statement(非法的continue语句:没有周围的迭代语句)
// // 想要实现 continue的效果,可以使用 return;
// }
if(index == 4) {
// break; // 不能使用break,报错:Uncaught SyntaxError: Illegal break statement(非法break语句)
return; // 这个return 不能结束循环,只能结束当前循环,与 continue 的效果一样;
}
console.log('item', item);
console.log('index', index, 'index 类型', typeof index);
console.log('currArr', currArr);
});
console.log('newArr', newArr);
console.log(arr); // 打印原数组 [6, 13, 19, 20, 21, 91]
- 使用
continue
时:
- 使用
break
时:
- 使用
return
时:
如图所示:
forEach()
循环不能使用break
; 不能使用continue
,想要实现continue
的效果可以使用
return
替代;
8、map( ) 循环
功能:循环遍历数组中的每个数据,并对数据进行运算,不改变原数组,返回一个与原数组相同 length
的新数组;
参数:回调函数;
返回值:新的数组
语法:map( 回调函数 )
;
注意: map() 不会对空数组进行检测。
注意: map() 不会改变原始数组。
示例:
var arr = [5, 12, 18, 19, 20, 90];
var newArr = arr.map((item, index, currArr) => {
// if(index == 2) {
// continue;
// // 不能使用 continue,
// // 会报错:Illegal continue statement: no surrounding iteration statement(非法的continue语句:没有周围的迭代语句)
// // 想要实现 continue的效果,可以使用 return;
// }
if(index == 4) {
// break; // 不能使用break,报错:Uncaught SyntaxError: Illegal break statement(非法break语句)
return 321; // 这个return 也不是结束循环,只是在满足当前条件时,给map循环返回值的新数组中加入此返回值,效果也和continue 类似;
}
console.log('item', item);
console.log('index', index, 'index 类型', typeof index);
console.log('currArr', currArr);
return item+1;
});
console.log('newArr', newArr); // 打印返回值 [6, 13, 19, 20, 21, 91]
console.log(arr); // 打印原数组 [5, 12, 18, 19, 20, 90]
map()
循环与 forEach()
循环在使用 break、continue
的效果一样,在此便不做多演示了,但在使用 return
时却有不同,因为 map()
循环是通过 return xxx
来返回一个新的数组的;
如图所示:
9、filter( ) 循环
功能:检测过滤数组数据,并返回符合条件的所有数据的新数组,不改变原数组;
参数:回调函数;
返回值:新的数组;
语法:filter( 回调函数 )
;
注意: filter() 不会对空数组进行检测。
注意: filter() 不会改变原始数组。
示例:
var arr = [5, 12, 18, 19, 20, 90];
var newArr = arr.filter((item, index, currArr) => {
// if(index == 2) {
// continue;
// // 不能使用 continue,
// // 会报错:Illegal continue statement: no surrounding iteration statement(非法的continue语句:没有周围的迭代语句)
// // 想要实现 continue的效果,可以使用 return;
// }
if(index == 4) {
// break; // 不能使用break,报错:Uncaught SyntaxError: Illegal break statement(非法break语句)
return; // 这个return 不能结束循环,只能结束当前循环,与 continue 的效果一样;
}
console.log('item', item);
console.log('index', index, 'index 类型', typeof index);
console.log('currArr', currArr);
return item >= 18;
});
console.log('newArr', newArr); // 打印返回值 [18, 19, 90]
console.log(arr); // 打印原数组 [5, 12, 18, 19, 20, 90]
在此,还需要注意一点: 工作中通常后端返回的数组并不是我们想要的,可能我们需要重组一下数据,那么就经常使用到
map
或者
filter
方法; 如:
- 假定
objArr
为后端返回的数据,但是我们可能需要在页面上自定义一个字段为age
,且这个自定义字段需要在某些条件下,那么在这里我们就可能会这样写:
let objArr = [
{ name: '小花', type: ''},
{ name: '大黄', type: 'dog'}
];
let newArr = objArr.map( item => {
if(item.type) {
item.age = 24;
return item;
}
});
console.log('newArr', newArr);
for(let i = 0; i < newArr.length; i++) {
console.log(newArr[i].age);
}
但是报错了!!! (抓狂)
为什么?(抓狂……)
- 这是因为
map()
循环是将原有数组“拷贝”一份成为一个新数组,什么意思?意思就是map()
循环返回的新数组和原数组的长度length
是一样的,所以当使用条件判断时,不满足的那一项会直接返回undefined
,undefined
当然而然的会找不到取值的字段名了。- 而
filter()
循环则不同,filter()
循环是将符合条件的返回出去形成一个新的数组,自然而然的能够取到值;所以只需要将map
改成filter
即可解决报错问题;
- 当然还有一个方法解决这个报错问题,就是将
return item
移到if
的外层,这个时候我们需要考虑的就是一个数据的问题,看你的需求是想要什么数据,filter()
是通过条件判断过滤后的数据问题,而
map()
则是将原数据改造成你想要的数据问题(length
不变)。
其实这个问题,在这几个方法的功能介绍里面已经用红色字体标出来。不知道小伙伴们有没有意识到~~~~~~~~
另外:请看这个案例
let list = []
for (let i = 0; i < 3; i++) {
let item = {
a: 1,
b: 2,
}
list.push(item)
}
console.log("list", list);
let arr = list.map( (item, index) => {
if(index == 2){
item.is = true;
}
return item;
})
console.log('arr', arr);
// let newarr = []
let newarr = arr.filter( item => {
let obj = {}
if(item.is) {
obj.c = item.a;
// newarr.push(obj);
return obj;
}
})
console.log('newarr', newarr);
打印结果如图所示:
是不是感觉到很奇怪,最终newarr
的结果不是[{c: 1}]
,这里还是和 filter
这个方法有关,filter 方法是在原数组中筛选过滤出符合条件返回一个新的数组,意思简单点就是说你这个新数组的每一项必须是原数组中存在的。
当然,有的时候我们需要重组数据,且把里面的键名换掉,我们可以这么做:定义一个空数组,然后在符合条件的时候把重组的数据push
到空数组里面即可;
let list = []
for (let i = 0; i < 3; i++) {
let item = {
a: 1,
b: 2,
}
list.push(item)
}
console.log("list", list);
let arr = list.map( (item, index) => {
if(index == 2){
item.is = true;
}
return item;
})
console.log('arr', arr);
let newarr = [] // 在这里定义一个空数组
arr.map( item => {
let obj = {}
if(item.is) {
obj.c = item.a; // 然后在符合条件时,把值push到空数组里面去
newarr.push(obj);
}
})
console.log('newarr', newarr);
结果如图所示:
forEach( )、map( )、filter( ) 总结
三者的相同点:
1)参数都是回调函数;
2)回调函数的参数也是一样的,item 参数代表数组中的元素,index 参数是代表下标,arr是数组;
3)三者都是遍历(即循环)数组。
三者的不同点:
1)forEach( ) 把原始数组的元素内容进行修改;
2)map( ) 原始数组不变,产生一个新的数组,新的数组是原始数组进行运算(加工)后的映射(也可以理解成为拷贝)的结果;
3)filter( ) 原始数组不变,过滤,把原始数组中每个元素满足条件的元素(是原数组中已存在的)挑出来,放在一个新的数组里。
附送小案例:九九乘法表
利用 for 循环的循环嵌套
效果图如下:
具体实现代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
span{
display: inline-block;
width: 100px;
height: 50px;
border: 1px solid #000;
margin:2px 2px;
text-align: center;
line-height: 50px;
}
</style>
</head>
<body>
<script>
for (var n = 9; n >= 1; n--){
for (var i = 9; i >= n; i--){
document.write( '<span>' + n + '*' + i + '=' + n * i + ' </span>')
}
document.write('<br>')
}
</script>
</body>
</html>
如有不足之处,望大家多多指点,谢谢!