for循环的理解及在for循环中使用let和var

for语句是循环命令的另一种形式,可以指定循环的起点、终点和终止条件。它的格式如下。

for (初始化表达式; 条件; 递增表达式)
// 或者
for (初始化表达式; 条件; 递增表达式) {
}

for语句后面的括号里面,有三个表达式。

初始化表达式(initialize):确定循环变量的初始值,只在循环开始时执行一次。

条件表达式(test):每轮循环开始时,都要执行这个条件表达式,只有值为真,才继续进行循环。

递增表达式(increment):每轮循环的最后一个操作,通常用来递增循环变量。

执行顺序:

第一步,初始化表达式赋初值;

第二步,判别现有的值是否满足条件表达式,若其值为真,满足循环条件,则执行循环体内语句,然后执行递增表达式,然后进入第二次循环。若判断条件表达式的值为假,就终止for循环,执行循环体外语句。

下面是一个例子。

var x = 3;
for (var i = 0; i < x; i++) {
  console.log(i);
// 2

上面代码中,初始化表达式是var i = 0,即初始化一个变量i;测试表达式是i < x,即只要i小于x,就会执行循环;递增表达式是i++,即每次循环结束后,i增大1。

所有for循环,都可以改写成while循环。上面的例子改为while循环,代码如下。

var x = 3;
var i = 0;
while (i < x) {
  console.log(i);
}

for语句的三个部分(initialize、test、increment),可以省略任何一个,也可以全部省略。

for ( ; ; ){
  console.log('Hello World');
}

上面代码省略了for语句表达式的三个部分,结果就导致了一个无限循环。

另外:for循环语句中如果有函数中有循环的变量值,只要不是在这个循环里直接调用这个函数,那么这个循环的变量值就是循环结束的值,如:

上面代码中如果循环变量i在for循环中是0,1,2当循环结束后的值是3,所以当你在循环语句外调用这个函数a得到的就是循环结束的值3。

但是如果你直接在循环体内执行那就能得到每次循环的i的值

在for循环表达式中使用var声明表达式变量

就像上面的代码,在for循环中使用var,因为var是全局变量,所以循环结束后值会被覆盖掉,比如初始值i=0;i<3那么最后循环结束后i的值就会也只能是3,通过下面的代码能帮助我们更好的理解

for(var i = 0; i<5;i++){
  setTimeout(()=>console.log(i),0) // 5 5 5 5 5
}

上面的代码因为setTimeout是一个异步,所以它拿到的是循环结束后的i的值,因为上面我们说的var是全局变量会被覆盖掉所以最后的i值是5,而且一共循环了5次(0-4),所以会打印触五个5。


在for循环表达式中使用let声明表达式变量

因为let有自己的作用域块,所以在for循环表达式中使用let其实就等价于在代码块中使用let,也就是说

  1. for( let i = 0; i< 5; i++) 这句话的圆括号之间,有一个隐藏的作用域
  2. for( let i = 0; i< 5; i++) { 循环体 } 在每次执行循环体之前,JS 引擎会把 i 在循环体的上下文中重新声明及初始化一次。
var liList = document.querySelectorAll('li') // 共5个li
for(let i = 0;i<liList.length;i++){
 liList[i].onclick = function(){
    //这里的i是循环结束的i,因为监听事件是异步
    console.log(i)//0 1 2 3 4
//上面代码其实就等价于
for (let i = 0;i<liList.length;i++){
 let i = 隐藏作用域中的i(表达式中的i)
 liList[i].onclick = function(){
    console.log(i)
//你也可以在代码块中使用另一个let变量接受每一个i
for(let i = 0;i<liList.length;i++){
 //这里能很直观的看出j有自己的作用域
 let j = i;
 liList[j].onclick = function(){
    console.log(j)
//你也可以当成是声明了五个let块级作用域
{let i = 0}