cover

第一个例子

var a = [];  
for (var i = 0; i < 10; i++) {  
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10  

这是一段很经典的代码。之前一直无法理解,为什么console.log(i)的时候,i是i而不是a[i]所对应的值。

看似数组a中,每个元素都取到了相应的i

但是。数组a其实取到的是那个函数,i的话,其实是在运行的时候读取的。

如果运行a[6]();函数,那么实质上i从全局作用域中读取,此时的i已经是循环结束后的那个10了。

对此,只需稍作改造。

var a = [];  
for (var i = 0; i < 10; i++) {  
  a[i] = (function(num) {
    return function() {
      console.log(num)
    }
  })(i)
}
a[6]();  

我们把

  a[i] = function () {
    console.log(i);
  };

改成了

  a[i] = (function(num) {
    return function() {
      console.log(num)
    }
  })(i)

此时,循环中的i作为num的值传递给匿名立即执行函数,函数运行后返回一个函数,保持着对传进来的num的引用。十个函数便有十个闭包。

此时a[i]又等于函数。运行a[i]时,就会读取闭包所保存的变量。

第二个例子

/*
 * 本意是想,每一秒输出一个i。
 * 因为setTimeout的时间定位i*1000,
 * 也就是1,2,3……秒时各执行一次
 */
for (var i = 0; i < 5; i++) {  
  setTimeout(function() {
    console.log(i);
  }, i * 1000);
}

本意是想,每一秒输出一个i。但却会输出5个5。

原因和上面的一样,for循环在一瞬间执行完毕。console.log(i);中的i,则会沿着原型链向上查找。

此时全局变量中的i,已经是for循环结束后的i了。

自己的解决方法

for (var i = 0; i < 5; i++) {  
  (function(num) {
    return (function() {
      setTimeout(function() {
        console.log(num);
      }, num * 1000);
    })()
  })(i)
}

这是一开始自己写的解决方法。。虽然能用,但是一大坨看起来很不爽。于是看了看别人写的,顿时那个汗颜啊。

for (var i = 0; i < 5; i++) {  
  (function(num) {
    setTimeout(function() {
      console.log(num)
    }, num * 1000)
  })(i)
}

自己的错误在于return了一个IIFE函数。事实上是没有必要的。循环中直接执行就行。

感想

到这儿的话,总算对闭包有所了解了。

包括之前迷迷糊糊的this值的四种状态,闭包会导致内存泄漏等问题。总算是了解了个清楚。下回写博客时候,给一起写出来。

let的基本用法,也是总算对闭包有所了解的地方

扫描二维码,分享此文章

Lxxyx's Picture
Lxxyx