JS函数调用时机不同,结果不同

**

解释如下代码为什么会打印6个6

  1. let i = 0
  2. for(i = 0; i<6; i++){
  3. setTimeout(()=>{
  4. console.log(i)
  5. },0)
  6. }

解释:首先,setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。上面代码中,setTimeout函数接受两个参数,第一个参数func | code是将要推迟执行的函数名或者一段代码,第二个参数delay是推迟执行的毫秒数。 setTimeout(fn,0) 作用是让fn在现有的任务(脚本的同步任务和“任务队列”中已有的事件)一结束就立刻执行。也就是说,setTimeout(f,0)的作用是,尽快地执行指定的任务。

setTimeout的延迟不是绝对精确的;setTimeout的意思是传递一个函数,延迟一段时候把该函数添加到队列当中,并不是立即执行;所以说如果当前正在运行的代码没有运行完,即使延迟的时间已经过完,该函数会等待到函数队列中前面所有的函数运行完毕之后才会运行;也就是说所有传递给setTimeout的回调方法都会在整个环境下的所有代码运行完毕之后执行;

参考:如何理解setTimeout里面的异步?
所以, 当for循环执行到setTimeout时,会被浏览器丢到另一个任务队列里,浏览器这时候会继续执行for循环。每一次for循环的时候,setTimeout都被执行一次,但是里面的代码没有被执行,而是被放到了任务队列里面,等待执行,for循环执行了6次,就被放到任务队列里面6次,当for循环执行完成后,才进入到任务队列里面执行,此时经过for循环后,i的值已经变为6,所以console.log(i)的值全为6。

写出让上面代码打印 0、1、2、3、4、5 的方法

  1. for(let i = 0; i<6; i++){
  2. setTimeout(()=>{
  3. console.log(i)
  4. },0)
  5. }
  6. // 打印出0、1、2、3、4、5

解释:let关键字声明的变量i,是块作用域,也就是只能在代码块中有效。每一次for循环时生成的新的i值都会被保存在当前的运行环境中,所以当for循环结束后,执行setTimeout中的console.log(i)时,打印的是当时保存的i值。整个过程中保存了7次i值,分别是0、1、2、3、4、5、6


除了使用 for let 配合,还有什么其他方法可以打印出 0、1、2、3、4、5

  • 闭包+立即执行函数

    1. let i
    2. for(i = 0; i<6; i++){
    3. !function(a){
    4. setTimeout(()=>{
    5. console.log(a)
    6. },0)
    7. }(i)
    8. }
    9. //打印出0、1、2、3、4、5
  • 利用 setTimeout 的第三个参数, 将i传进去(附加参数,一旦定时器到期,它们会作为参数传递给function)

    1. let i
    2. for(i = 0; i<6; i++){
    3. setTimeout((a)=>{
    4. console.log(a)
    5. },0,i)
    6. }
    7. //打印出0、1、2、3、4、5
  • 使用 const 关键字,const关键字和let类似,也具有块作用域,声明后就要赋值

    1. let i
    2. for(i = 0; i<6; i++){
    3. const x = i
    4. setTimeout(()=>{
    5. console.log(x)
    6. })
    7. }
    8. //打印出0、1、2、3、4、5

    以上三种方法参考知乎文章JS 函数的执行时机