你的位置:首页 > Java教程

[Java教程]对闭包的理解


首先看一段代码

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

这段代码输出0, 1, 2, 3, 4, 5, 6, 7, 8, 9

接着

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

这段代码输出的全是10

这里引用原文的话:详情https://www.douban.com/note/293295975/

因为setTimeout是异步的!
   你可以想象由于setTimeout是异步的, 因此我们将这个for循环拆成2个部分
   第一个部分专门处理 i 值的变化, 第二个部分专门来做setTimeout
   因此我们可以得到如下代码
   // 第一个部分
   i++;
   ...
   i++; // 总共做10次

   // 第二个部分
   setTimeout(function() {
      console.log(i);
   }, 1000);
   ...
   setTimeout(function() {
      console.log(i);
   }, 1000); // 总共做10次

   这样一拆后, 我相信你肯定知道之前那个for循环的运行结果了.
   由于循环中的变量 i 一直在变, 最终会变成10, 而循环每每执行setTimeout时, 其中的方法还没有真正运行, 等真正到时间执行时, i 的值已经变成 10 了!
   i 变化的整个过程是瞬间完成的, 总之比你异步要快, 就算你setTimout是0毫秒也一样, 会先于你执行完成.

接下来上自己的例子

var nodes = document.getElementsByTagName("li");      for(i = 0;i<nodes.length;i+= 1){        nodes[i].onclick = function(){          console.log(i+1);//不用闭包的话,值每次都是4        };      }

这段代码无论点击哪个 li 都只输出4;

这里我个人的理解,根据浏览器JS解析引擎;

JS解析引擎一开始并不是直接从上到下开始执行代码

而是搜集代码中的全局变量和函数块存起来。

接着再从上到下执行代码。

第一行代码

nodes = document.getElementsByTagName("li");

给变量nodes赋值,接着for循环

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

 nodes[i].onclick为点击事件添加处理函数(注意!function(){ console.log(i+1);}并没有执行,而是函数的引用赋值给nodes[i].onclick而已)
所以当点击事件触发,浏览器执行  function(){ console.log(i+1);}时查找 i 的值就是for循环执行后的值。
也可以理解成这样
  function a(){ console.log(i+1);};
 for(i = 0;i<nodes.length;i+= 1){        nodes[i].onclick = a;      }
 

这就是说明  console.log(i+1) 只输出循环的之后的值得原因在于:(!function(){ console.log(i+1);}并没立即执行)

所以为了解决该问题  

i 作为参数传入来固定这个变量的值, 让其保留下来

需要作以下修改

 

function a (i) {    console.log(i);  }  for(var i = 0; i < 10; i++) {  setTimeout(a(i), 1000);  }

 

这样已经可以输出0, 1, 2, 3, 4, 5, 6, 7, 8, 9了。

再接着修改

for(var i = 0; i < 10; i++) {  setTimeout((function a (i) {console.log(i);})(i), 1000);  } 

var nodes = document.getElementsByTagName("li");      for(i = 0;i<nodes.length;i+= 1){        nodes[i].onclick = (function(){          console.log(i+1);//不用闭包的话,值每次都是4        };)(i)      }

其实上诉解决办法就是闭包啦。

不过这样也会带来副作用,例如setTimeout并没有延时就直接输出了,点击事件并没有点击也触发了。

 闭包的作用

先上代码

function say667() {      // Local variable that ends up within closure      var num = 666;      var sayAlert = function() {        alert(num);      }      num++;      return sayAlert;    }     var sayAlert = say667();     sayAlert()//执行结果应该弹出的667

执行say667()后,say667()闭包内部变量会存在,而闭包内部函数的内部变量不会存在
        使得Javascript的垃圾回收机制GC不会收回say667()所占用的资源
        因为say667()的内部函数的执行需要依赖say667()中的变量
        这是对闭包作用的非常直白的描述