JS 函数的执行时机

499 阅读3分钟

在学习JS函数的过程中,透彻理解JS函数的执行时机是非常重要的,JS函数执行的时间不同,得到的结果也就可能不同。如下例所示:

①:

let a = 1
function fn(){
  console.log(a)
}
fn()//打印出1

②:

let a = 1
function fn(){
  console.log(a)
}
a = 2
fn()//打印出2

③:

let a = 1
function fn(){
  console.log(a)
}
fn()//打印出1
a = 2

④:

let a = 1
function fn(){
  setTimeout(()=>{
    console.log(a)
  },0)
}
fn()//打印出2
a = 2

其中,比较难理解的可能就是④了,在④中,fn内的函数里调用了setTimeout方法,该方法内的第二个参数是0,这里的意思是尽快打印出 a 的值,尽快指的是当前代码执行完毕后立刻打印,当调用完函数fn的时候,程序会接着执行下面的代码,于是 a 的值就变成了2,下面的代码执行完后立即打印,此时打印的值也就是2了。 再来理解下面的代码:

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

初学时肯定会认为这段代码会依次打印出0/1/2/3/4/5,然而这段代码执行的结果却是打印出6个6,原因是在for循环中使用setTimeout函数,每一次循环就相当于告诉JS引擎在当前代码执行完毕后立刻打印出变量 i 的值,一共告诉了6次,此处就意味着在for循环执行完毕后需要打印出6次 i 的值,而根据for循环的执行特点可知,当循环结束后 i 的值一定会变成6,因此执行结果就是打印出6个6。

那么要怎样才能依次打印出0/1/2/3/4/5呢?这就需要使用 for let 配合了,看下列代码:

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

在这段代码的执行过程中,JS 引擎在每次执行循环体之前,会把 i 在循环体的上下文中重新声明及初始化一次,更多内容请参见:方应杭:我用了两个月的时间才理解 let 。需要注意的是,上述代码中的let如果换成了var,依然会打印出6个6。

如果不使用 for let 配合,依然将 i 的声明放在循环外,那么怎样才能依次打印出0/1/2/3/4/5呢? ① 可以在for循环中重新声明一个变量 j ,让 j 的值等于 i ,而每次打印 j 的值,代码如下:

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

② 可以使用setTimeout函数的第三个参数,代码如下:

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

③ 在for循环中再定义一个立即执行函数,代码如下:

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

若有任何问题,欢迎大家留言,渴望进步、共同成长!