循环(for while)、函数、循环的退出、return是什么、递归
遍历
线性-循环
1、for循环(for三元素的具体含义)
for(let i=0; i<3; i++) {console.log(i)}
这个循环体的执行顺序是:
1、let i=0 声明变量,并初始化为0
2、i<3 的判断,如果条件成立则进入循环体
3、循环体内执行代码
4、i++
5、i<3的判断,如果条件成立则进入循环体
6、循环体内执行代码
….
2、while
let i=0;
while(i<3) {
console.log(i)
i++;
}
同样是 先声明变量, 条件判断,如果满足则进入循环体
3、es6+的遍历有
- arr.forEach
- arr.map
- for key in arr
- for val of arr
- arr.reduce
具体用法可点击这里
非线性-递归
递归的运行不是黑盒,抽象点的理解递归是竖向的遍历,是基于递归栈,理解递归的每一层的概念(盗梦空间的每一层),再深入递归的表现形式变种。
printf(0); # 向函数传递初始值
function printf(i) { # 变量i
if (!(i<3)) return; # 递归终止条件 等价于 i<3则可以进入循环,否则退出循环
console.log(i);
i++;
printf(i); # 进入下一个循环,下一层迭代
}
表达式辨析
break VS continue
1、break: 退出循环,结束循环
for(let i=0; i<3; i++) {
if (i===2) break;
console.log(i)
}
# 加break之前输出:0 1 2
# 加break之后输出:0 1
2、continue:此次迭代continue后面的代码不执行
for(let i=0; i<3; i++) {
if (i===2) continue;
console.log(i)
}
# 加continue之前输出:0 1 2
# 加continue之后输出:0 3
# 等效于
for(let i=0; i<3; i++) {
if (i==2) {
// .. 不做任何事
}
else {
console.log(i)
}
}
3、辨析:break、continue、return的区别
**
const arr = [
['00','01','02'],
['10','11','12']
]
function print() {
for(let i=0; i<2; i++) {
for(let j=0; j<3; j++) {
// if (j==1) return; 输出 00
// if (j==1) break; 输出 00 10
// if (j==1) continue; 输出 00 02 10 12 => 即第二列不打印
console.log(arr[i][j]);
}
}
}
for 循环体的执行顺序是:
1、i在第一行,j 从第1列到第3列遍历
2、i在第二行,j从第1列到第3列遍历
所以
break:00 10 是因为
- i遍历第一行,j 从第1列到第3列遍历,当遍历到第2列时 breack 当前层循环退出, 所以第一行只输出了00
- 开启i的第二次遍历,i遍历第2行,j 从第1列到第3列遍历,遇到第2列退出j的循环,所以第二行输出了10
continue:00 02 10 12
当内层的j的循环遇到2时,并不是退出当前层,而是不做啥事,j++,重新进入循环体打印输出第3列
return: 00
直接结束所有循环不再迭代, 无论当前循环被嵌入至第几层深度
总结:
1、break 提出当前层循环,结束当前层循环
2、continue 符合continue条件的循环,continue之后的代码不执行
3、return 直接退出全部循环
同样 breack、continue、return 也可以用在while里
function print() {
let i = 0;
while(i < 2) {
let j = 0;
while(j < 3) {
// if (j==1) break;
# j=1 continue 之前要手动j++ 否则会进入死循环
// if (j==1) {
// j++;
// continue;
// };
// if (j==1) return;
console.log(arr[i][j]);
j++;
}
i++;
}
}
break: 00 10
continue: 00 02 10 12
return:00
i++ VS ++i
let arr = [0,1,2,3];
let i=0;
arr[++i]=4;// arr=[0,4,2,3]; i=1
let arr2 = [0,1,2,3];
let j=0;
arr2[j++]=4;// arr2=[4,1,2,3];j=1
let m=2, n=2;
while(n--){
console.log(1); // 打印几次 2次
}
while(n) {
console.log(1); // 2次
n--;
}
while(--n) {
console.log(1); // 1次
}
function getData(i) {
if (i>10) return;
getData(i++); vs getData(++i);
# ++i 是加了 之后 再进入函数递归,i++ 则进入函数之后才i+1,
# 所以当是i++时,递归永远没有结束的一天即爆栈
}
while(n—) VS while(—n)
let count = 3;
while(count--) {
console.log(count);
}
# 打印: 2 1
let count2 = 3;
while(--count2) {
console.log(count2);
}
# 打印: 1
从上面的例子可以知道:
1、while() 括号里的表达式,是先执行再进入循环体的
2、while(n—) 与 while(—n) 差别
while(n—) 是 先判断while(n)此时的n>0与否,然后n=n-1; 然后再进入循环体
while(—n) 是 先n=n-1, 然后判断while(n) 此时的n是-1之后的n,然后再进入循环体
所以,while(n—) 比 while(—n) 多一次打印
let count = 3;
while(count--) {
console.log(count);
}
// 等效于
while(count) {
count--;
console.log(count);
}
但如果把count—放入循环体内执行,一定要保证每次迭代count都能得到更新(不断向推出循环的临界值靠近),否则很容易写成死循环
比如下面的代码块是死循环看出来了么~
let count = 3;
while(count) {
if (count==2) continue;
console.log(count);
count--;
}
总结下,while需要注意的点就是:保证循环体的退出
way1:手动调用breack or return;
way2:条件变量不断逼近临界值, 保证条件变量无论发生什么都能被更新迭代
Boolean判断
空值
空数组、空对象
面试常考的代码不是背出来的-空值判断
