闭包

    <ul>
        <li class="id1">这是一个例子</li>
        <li class="id2">这是一个例子</li>
        <li class="id3">这是一个例子</li>
    </ul>
以上的代码,我们要实现的一个功能是,实现点击每个 li 标签时候弹出当前点击的那个 li 标签的索引,这里我们用的是 javascript 的原生方法。
刚开始的时候,我自以为是的用了下面的方法,相信在没有了解闭包之前的朋友们应该都有这么写过
    var len = document.querySelectorAll('li').length; //获取li标签的长度
    var obj =  document.querySelectorAll('li'); //取得li这个对象
    for(var i = 0;i < len ; i++){
        obj[i].onclick=function(){
            alert(i);
        }; 
    } 
错误理解,循环三次,依次对 li 标签绑定点击事件,在绑定的时候弹出循环的索引(此时循环的索引是和 li 标签的索引是一致的)。
现在当我们去点击的时候,会发现无论我们点击那个标签,弹出的都是3,显然这不是我们任何一个表现的索引,但是我们发现我们标签的长度是3,最大的索引是2,而这里的3正好是 for 跳出循环的值。为了验证这个说法,我们逐步次添加标签,再次证明了我们的说法。
为什么我们在点击的时候,取不到正确的 i 值?
1495788014(1).jpg
for循环打断点,我们发现i确实经历了从0到2的步骤,并且给obj[0] obj[1] obj[2]都依次绑定了点击事件,到i到3的时候,不满足循环条件,所以跳出。这里就引入了我们的另外一个概念,同步和异步。
点击事件是一个异步事件,初始化的时候,我们绑定了点击事件,但是并没有触发点击事件的发生。因为是点击时异步执行的,javascript是单线程,需要把for循环执行完毕,当我们触发点击事件的时候,此时i值已经到达了3,之前的i并没有被保存下来,跳出了判断。我们要做的就是,在绑定事件的时候,也把绑定时的值传到事件内部。
在一个函数内部,添加一个函数,内部函数可以访问外部函数的变量(相反,外部函数不可以访问内部函数),这就形成了一个闭包。
  var len = document.querySelectorAll('li').length;
    var obj =  document.querySelectorAll('li');
    console.log(document.querySelectorAll('li').length);
    for(var i = 0;i < len ; i++){
      (function(i){
        obj[i].onclick=function(){
            alert(i);
        };
      })(i);
     
    } 
这里通过自执行的匿名函数,就实现了每次循环的时候,把i值传到事件内部。循环给obj绑定事件的时候,就把i给保存了下来。

闭包的深入理解

游览器是实现时就认为只要存在访问上层作用域的可能性,就会被当成是一个闭包。

总结一下闭包:

  1. 闭包是在函数被调用执行的时候才被确认创建的。
  2. 闭包的形成,与作用域链的访问顺序有直接关系。
  3. 只用内部函数访问了上层作用域链中的变量对象时,才会形成闭包,因此我们可以利用闭包来访问函数内部的变量。
Array.proyotype.map这个方法可以很方便的解决上面的问题,而不用用到闭包的概念
 var arr = [obj[0],obj[1],obj[2]];
    arr.map(function(currentValue,index,array){
       currentValue.onclick=function(){
         alert(index);
       }
    });
因为这里这个方法的其中一个参数就是索引,合理的避开了上面的问题。当然这里的前提是要先把每个对象转成数组。因为这是在Array对象原型上的方法。
    var len = document.querySelectorAll('li').length;
    var obj =  document.querySelectorAll('li');
    console.log(document.querySelectorAll('li').length);
    var arr = [];
    for(var i = 0;i < len ; i ++){
        arr.push(obj[i]);
    }
    arr.map(function(currentValue,index,array){
       currentValue.onclick=function(){
         alert(index);
       }
    });

推荐阅读更多精彩内容