• JS闭包那些事


          关于闭包,我曾经一直觉得它很讨厌,因为它一直让我很难搞,不过有句话怎么说来着,叫做你越想要一个东西,就要装作看不起它的样子。所以,抱着这个态度,我终于掳获了闭包。

      首先来认识一下什么是闭包,闭包,一共有三大特征:

       1 函数嵌套函数
       2 内部的函数可以引用外部函数的参数和变量
       3 参数和变量不会被垃圾回收机制所收回

    举个栗子:

    function aaa(){ 
       var b = 5;
        function bbb(){
    
            b++; 
    
            alert(b);
    
        }
    
    }
    
    aaa(); 

    这个栗子就是很明显的闭包,函数里面嵌套函数,同时内部的函数bbb又可以访问到外部函数aaa中的变量,至于第三个特征,我们都知道在JS解析机制中,函数内的变量在函数调用完后会被销毁,但在这里b并没有被销毁,因为他还会被里面的函数bbb引用,那么怎么证明呢?

    首先,我们要注意,在上面这个栗子中,是不会弹出值的,因为里面的函数bbb,只是声明了,并没有被调用,我们都知道,函数它不会主动执行的,那么怎么执行呢,看下面:

    function aaa(){ 
    
        var b = 5;
         function bbb(){ 
              b++;
    
            alert(b); // 6
    
         }
        return bbb;
    
    }
    
    var c = aaa(); // 此时aaa被执行 同时把返回结果bbb 赋给c 
    c(); //6

    将里面的函数作为返回结果,然后可以调用,这种经常会遇见,当aaa执行后,返回结果为一个函数,然后再调用。e而且会弹出6,因为它的值被里面的函数改变。

    现在知道了闭包的特征,那最重要的是知道它的好处和应用:

      1 希望一个变量长期驻扎在内存当中

      2 避免全局变量的污染

      3 私用成员的存在

    1 首先呢,我们肯定有过这样的需求,我们需要这样一个变量,在全局的很多地方都可以被改变,于是我们会声明一个全局变量,但是也就因为它可以在任何地方被改变,所以很容易出问题被污染。我们既希望它可以不被污染,又希望它可以在很多地方都能访问到,这样就闭包就产生了作用。

    看个栗子。

    function aaa(){ 
         var a = 1;
        return function(){ 
        a++;
        alert(a);
        }
    }
    var c = aaa();
    c(); //2
    c(); //3
    c(); //4

    当我们每调用一次,a的值就被累加,同时它又没有被污染,因为闭包的第三个特性,它的变量不会被垃圾机制回收,所以每调用一次都会在原来的基础上加1。

    其实这个时候改写成更简单的方式,就是改成函数表达式:

    var aaa =(function(){
    var a = 1;
    return function(){ 
    a++;
    alert(a);
    }
    })();
    
    aaa(); //2
    aaa(); //3

    这样是不是省事很多了呢,如果不明白函数表达式,可以参考我前面的文章,讲的很清楚。

    2 我们通过一个例子来看看第三个好处

    var aaa =(function(){
      var a = 1;
      function bbb(){ 
        a++;
        alert(a);
      }
     function ccc(){ 
      a++;
      alert(a);
      }
      return { 
        b:bbb,
        c:ccc
      } // 返回json对象
    
    })();
    
    aaa.b(); // 2
    aaa.c(); // 3

    当在一个函数里声明了多个函数,可以通过json的格式返回,然后我们就可以在外面这样调用,这些函数就成了函数aaa的私有成员,如果对json不太了解,可以我看看w3cschool的介绍,很好理解。

    3 下面我们看第三个栗子。它非常优秀的体现了闭包的优点,就是我们经常写的选项卡切换的例子,一般我们会循环给每个列表加上一个索引,通过闭包就不用再加索引了。

    在循环中直接找到对应元素的索引。如下 没循环一次 内部函数调用一次 将i的值直接作为参数传进去。

    window.onload = function(){ 
      var aLi = document.getElementsByTagName('li');
      for(var i=0;i<aLi.length;i++){ 
        (function(i){ 
          aLi[i].onclick = function(){ 
            alert(i);
          }
        })(i);
      }
    
    }

    上面代码就是在点击事件的函数的外面再加上一个函数,就形成了闭包,然后每循环一次,通过函数表达式的形式,将i传进去,而通过闭包特性可以知道,事件绑定的函数就可以访问外面函数里面的参数i。

    其实还有另一种写法:

    window.onload = function(){ 
      var aLi = document.getElementsByTagName('li');
    
      for(var i=0;i<aLi.length;i++){ 
        aLi[i].onclick = (function(i){ 
          return function(){
          alert(i);
          }
        })(i);
      }
    
    }

    这种方法的原理是这样的,首先将一个函数表达式的调用赋给了点击事件,我们都知道,当事件后面等于的不是函数名,而直接是调用的话,那不点击,函数也已经执行了,所以在循环的时候,i已经作为参数,传进了函数表达式,而这个函数表达式的返回值又是一个函数,函数嵌套,闭包关系,这个函数可以访问外面函数传进来的每个i。

    如果是初学者,不熟悉选项卡,可以先去了解选项卡原理。

    闭包需要注意的地方(这个先做简单了解 后续会补充详细)

    1 在IE下有可能引发内存泄漏 (内存泄漏指当你的页面跳转的时候 内存不会释放 一直占用你的CPU 只有当你关闭了浏览器才会被释放)

    内存泄漏产生的条件:

    当获取一个节点或者一组节点 然后又给这个节点添加了属性( 比如事件) 而在事件内部你又引用外部的东西。

    解决;
    1 在页面未加载的时候将添加的属性去掉

    window.onunload = function(){ 
        odiv.onclick = null;
    }
    
    2 var id = odiv.id;
    odiv.onclick = function(){
    
      alert(id);
    }
    odiv = null;
  • 相关阅读:
    (转)expfilt 命令
    (转)第二十三节 inotify事件监控工具
    数据结构之平衡二叉树(AVL)
    安装apache2.4.10
    centos下编译安装mysql5.6
    随机 I/O & 顺序 I/O
    什么是mysql中的元数据
    linux中mail函数不能发送邮件怎么办
    检测MYSQL不同步发邮件通知的脚本
    mysql自动备份策略
  • 原文地址:https://www.cnblogs.com/moqing/p/5588701.html
Copyright © 2020-2023  润新知