循环(for while)、函数、循环的退出、return是什么、递归

遍历

线性-循环

1、for循环(for三元素的具体含义)

  1. for(let i=0; i<3; i++) {
  2. console.log(i)
  3. }

这个循环体的执行顺序是:
1、let i=0 声明变量,并初始化为0
2、i<3 的判断,如果条件成立则进入循环体
3、循环体内执行代码
4、i++
5、i<3的判断,如果条件成立则进入循环体
6、循环体内执行代码
….
image.png

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判断

空值

空数组、空对象
面试常考的代码不是背出来的-空值判断