JS 函数的执行时机

学习循环的时候大家应该都试过类似下面的代码:

for(var i=0; i<6; i++){
    console.log(i)
}

很明显运行代码会依次打印出0-5,代码执行顺序也很清楚,每次加一都执行一次打印。

但是随着我们学习的深入我们会碰到一些并非依次执行的例子,比如用for循环给每个li添加事件

for(var i = 0; i < aLi.length; i++){
    aLi[i].onclick = function(){
        console.log(i)
    }
}

这是运行上面的代码你会发现点击每一个li都是打印最后一个的索引值。

因为循环只是给每个li添加了事件,但是事件并不是立即执行的,然而i却是全局变量,到需要执行的时候i已经变成了aLi.length,这类问题可以总结成如下情况:

for(var i = 0; i<6; i++){
  setTimeout(()=>{//并非按顺序依次执行,会等到for循环结束再打印
    console.log(i)
  },0)
}

这种问题的本质就是利用var声明的 i 是一个全局变量,全局变量在for执行时会得到最后一个数值(aLi.length),所以会产生得不到每个中间值,解决问题的本质是要将 i 转换成局部变量

方法一:var改成let

for(let i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

每次循环会多创建一个i让它留在代码空间直到代码执行完毕

注意let i要声明在for的作用域里面,不然i依然不是for的局部变量

let i = 0
for(i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

像上面代码i依然还是全局变量,运行结果还是打印6个6

方法二:自执行函数可以按循环顺序依次执行,所以利用自执行函数留下i,然后利用闭包把i传给里面的函数

for(var i = 0; i<6; i++){
  !function(a) {
    setTimeout(()=>{
        console.log(a)
    },0)
  }(i)
}

方法三:将每次i的值变成局部变量留下来

for(var i = 0; i<6; i++){
  let j = i //也可以声明数组把i留下来
  setTimeout(()=>{
    console.log(j)
  },0)
}

总结:循环里面的全局变量没有按照循环顺序执行时,后面是取不到当时的值的,如果要延迟执行需要创建一个局部变量保留住当时的值,可以使用for+let组合或者自执行函数+闭包组合

你可能感兴趣的:(JS 函数的执行时机)