zoukankan      html  css  js  c++  java
  • 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;
  • 相关阅读:
    Android Gradle Plugin指南(五)——Build Variants(构建变种版本号)
    文件内容操作篇clearerr fclose fdopen feof fflush fgetc fgets fileno fopen fputc fputs fread freopen fseek ftell fwrite getc getchar gets
    文件操作篇 close creat dup dup2 fcntl flock fsync lseek mkstemp open read sync write
    嵌入式linux应用程序调试方法
    version control system:git/hg/subversion/cvs/clearcase/vss。software configruation management。代码集成CI:Cruisecontrol/hudson/buildbot
    最值得你所关注的10个C语言开源项目
    如何记录linux终端下的操作日志
    CentOS 5.5 虚拟机安装 VirtualBox 客户端增强功能
    sizeof, strlen区别
    C/C++嵌入式开发面试题
  • 原文地址:https://www.cnblogs.com/moqing/p/5588701.html
Copyright © 2011-2022 走看看