• js闭包之我见


      很久前的一个问题终于得以解决,内心是无比喜悦的,不多说,先上代码:

    1 function test(){
    2        for(var i=0;i<5;i++){
    3              window.onclick=function(){
    4                    alert(i);  
    5              }
    6        }         
    7 } 
    8 test()

    原意是点击第一下弹出i的值0,点击第二下弹出i的值1,一直到第五次点击弹出4。想象是美好的,现实却不按常规出牌,无论点击多少次,alert出来的值都是5。后来接触了js闭包,才知道原来是它搞的鬼。看了不少相关书籍和博客后,发现其实它也没有想象得那么难。

          我理解的闭包是可以访其他函数作用域的函数,在js中,通常通过在一个函数内部定义另一个函数来实现,如上例。提到闭包,应首先理解JavaScript特有的链式作用域,即一个函数作用域链的第一位是该函数本身的活动对象,第二位是包含该函数(即父函数)活动对象的索引,第三位是父函数的父函数活动对象的索引,以此类推,直至全局变量对象。注意,反之不成立,即父函数的作用域不包含子函数变量对象的索引。如图:

      在文章开头的例子中,每个匿名函数(循环了五次,相当于五个匿名函数)的作用域链中都保存着test的活动对象,所以它们引用的是同一个变量i。当函数返回后,i的值是5,此时,每个函数都引用着保存变量i的对象,所以在每个函数内部,i的值都是5.

          闭包具有两面性,有缺点,也有优点,在上例中因为对闭包使用不当没有达到想要的效果,但在一些特殊的应用中,只有用闭包才能实现相应的功能,接下来,就谈谈闭包的用途(优点)和注意事项(缺点)。

      1、用途

    •   从外部读取某个函数的局部变量。

        子函数可以访问父函数的作用域,那么如果全局作用域需要访问函数的局部变量怎么办?

    function myA(){
        var a=6;
        function myB(){
            alert(a);
        }
        return myB;
    }
    var result=myA();
    result();

        在上述代码中,无法直接访问myA内的变量a,但是可以再内部定义一个函数myB,该函数可以访问它的父函数myA的作用域内的变量a,然后将这个函数返回并赋值给result,相当于在全局作用域内调用myB。

    •   让某个局部变量始终保存在内存中

      js的内存回收机制是这样的:当调用一个函数时,为它的局部变量对象分配相应的内存空间,一旦调用结束,则收回为它分配的内存。但是这样会有一个bug,即若在函数A内定义了函数B,而B也调用了A内的局部变量x,且B随时可能被全局调用,因为A调用完毕后内存被收回,所以B访问不到x。js的解决办法是,在定义一个函数时,把这个函数和它可能使用到的变量(包括自身变量和父函数的变量),统统一起保存起来,构成一个闭包,即便当父函数调用完毕,这些变量占用的内存也不会被收回。只有当该函数不可能被调用时,才会收回这些内存。

      若需要随时访问某个局部变量,则可以通过该方法,构建一个闭包,让一个局部变量始终保存在内存中。

      2、缺点

    •   占用过多的内存

        上述js的回收机制说明,只要闭包不被销毁,它的作用域链所占用的内存便不能被释放,若过多地、无用地占用内存,对效率和性能极为不利。

    •   内存泄露

        由于IE9之前的版本对JavaScript和COM对象使用不同的垃圾回收机制(因为IE9之前中的部分对象不是原生JavaScript对象,如其中的BOM和DOM就是以c++的COM对象实现的,所以回收机制不同),所以只要在IE9之前版本中涉及COM对象,就会导致循环引用,以至于对象内存不能被收回。具体来说就是,如果闭包的作用域中保存着一个HTML元素,则意味着该元素将无法被销毁,例如:

    function f1(){
        var e=document.getElementById("top");
        element.onclick=function(){
              alert(e.id);
         };   
    }

        上述代码为e元素事件创建了一个闭包,即匿名函数,由于匿名函数包含着对f1活动对象的引用,只要匿名函数存在,e元素的引用次数至少为1,因此它占用的内存便永远不会被收回(IE9对COM对象的垃圾回收机制决定的)。该问题也有相应的解决方法,请读者自行思考。

      最后,关于闭包中this对象,需注意,由于匿名函数的作用域具有全局性,所以慎用闭包的this对象。没有用var声明的局部变量也具有全局性。

  • 相关阅读:
    面试系列14 redis的过期策略都有哪些
    面试系列13 redis都有哪些数据类型
    面试系列12 redis和memcached有什么区别
    面试系列11 缓存是如何使用
    面试系列10 es生产集群的部署架构
    linux命令中的“<”和“|”是什么意思?
    如何征服面试官,拿到Offer [转]
    ddt框架优化(生成html报告注释内容传变量)
    python笔记31-使用ddt报告出现dict() -> new empty dictionary dict(mapping) 问题解决
    测试中 unittest.main(verbosity=1) 是什么意思
  • 原文地址:https://www.cnblogs.com/nuannuan7362/p/5774857.html
Copyright © 2020-2023  润新知