“函数的调用时机不同,结果也会不同”我们先看几个例子理解一下这句话。

例1

  1. let a = 1
  2. function fn(){
  3. console.log(a)
  4. }
  5. fn()
  6. a = 2

上面的代码会打印出1,因为调用是在a=2的赋值之前。
例2

  1. let a = 1
  2. function fn() {
  3. setTimeout(() => {
  4. console.log(a)
  5. }, 0)
  6. }
  7. fn()
  8. a = 2

上面的代码会打印出2,因为setTimeout的意思是过一会再调用,他会在代码执行完再进行调用。

刚刚我们通过例1和例2中的setTimeout看到,代码的执行时机不一样所打印出的结果也不一样。那么看看下面的代码并思考一下,for循环为什么会打印出6 个 6。

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

setTimeout()实际上是设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。

因为事件循环机制的限制,以上代码的执行顺序是不可更改的,只要你使用了setTimeout,那就必须等到函数调用栈中可执行代码执行完毕之后,再去执行任务队列中的任务。也就是说setTimeout()会等for循环最后的条件判断运行完了之后再运行console.log(i),这时i的值为6,所以他会输出6个6。

用for let 配合可以解决这个问题
上面的代码没有成功用for let配合的原因是let的作用域没有到for里。

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

上面的代码会打印出0、1、2、3、4、5,因为JS在for和let一起用的时候,每次循环多创建一个i。

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

1.闭包函数
闭包函数,每一次创建都会存在一个自己的空间来存储唯一的值,将i的每一次执行for循环的值,传给不同创建的闭包函数,这样每一个闭包函数里存储的i值,就都不会一样.所以就是达到我们的想要的结果.

  1. for (var i = 1; i < 6; i++) {
  2. (function(i) {
  3. setTimeout(function() {
  4. console.log(i);
  5. }, 0);
  6. })(i);
  7. }
  1. 地址传递
    用另外一个变量储存值,每执行一次函数 i , j 就加一次,所以执行到setTimeout的时候,就会调用 i 函数
  1. function count() {
  2. var out = function(i) {
  3. setTimeout(function() {
  4. console.log(i);
  5. }, 0);
  6. };
  7. for (var j = 0; j < 6; j++) {
  8. out(j);
  9. }
  10. }
  11. count();
  1. 利用 setTimeout 的第三个参数,将i传进去
  1. for (i = 0; i < 6; i++) {
  2. setTimeout((j) => {
  3. console.log(j)
  4. }, 0, i)
  5. }

4.使用立即执行函数

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

总结

  1. 函数的调用时机不同,结果也会不同
  2. setTimeout()的执行时机
  3. for let 配合打印出 0、1、2、3、4、5
  4. 其他方法打印出 0、1、2、3、4、5