闭包的形成

闭包的形成

  • 闭包的形成与变量的作用域以及变量的生存周期密切相关。

变量的作用域

  • 变量的作用域,就是指变量的有效范围。我们最常谈到的是在函数中声明的变量作用域。
    当在函数中声明一个变量的时候,如果该变量前面没有带上关键字 var ,这个变量就会成为全局变量,这当然是一种容易造成命名冲突的做法。另外一种情况是用 var 关键字在函数中声明变量,这时候的变量即是局部变量,只有在函数内部才能访问到这个变量,在函数外面是访问不到的。代码如下:
    var func = function(){
    var a = 1;
     alert ( a ); // 输出: 1
    };
    func();
    alert ( a ); // 输出:Uncaught ReferenceError: a is not defined
  • JS中函数可以用来创造函数作用域。此时的函数像一层半透明的玻璃,在函数里面可以看到外面的变量,而在函数外面则无法看到函数里面的变量。这是因为当在函数搜索一个变量的时候,如果该函数内并没有声明这个变量,那么此次搜索的过程会随着代码执环境创建的作用域链往外层逐层搜索,一直搜索到全局对象为止。变量的搜索是从内到外而非外到内的。下面这段包含了嵌套函数的代码,也许能帮助我们加深对变量搜索过程的理解:
    var a = 1;
    var func1 = function(){
        var b = 2;
        var func2 = function(){
        var c = 3;
        alert( b ); // 输出:2
        alert( a ); // 输出:1
    }
    func2();
    alert( c ); // 输出:Uncaught ReferenceError: c is not defined
    };
    func1();

变量周期

  • 另外一个跟闭包有关的概念是变量的生存周期。对于全局变量来说,全局变量的生存周期当然是永久的,除非我们主动销毁这个全局变量。而对于在函数内用 var 关键字声明的局部变量来说,当退出函数时,这些局部变量即失了它们的价值,它们都会随着函数调用的结束而被销毁:
    var func = function(){
        var a = 1; // 退出函数后局部变量 a 将被销毁
        alert( a );
    };
    func();

//现在来看看下面这段代码:
    var func = function(){
      var a = 1;
        return function(){
         a++;
        alert( a );
         }
    };
        var f = func();
        f(); // 输出:2
        f(); // 输出:3
        f(); // 输出:4
        f(); // 输出:5
  • 跟之前的推论相反,当退出函数后,局部变量 a 并没有消失,而是似乎一直在某地方存活着。这是因为当执行 var f = func(); 时, f 返回了一个匿名函数的引用,它可访问到 func()被调用时产生的环境,而局部变量 a 一直处在这个环境里。既然局部变量所在的环境还能外界访问,这个局部变量就有了不被销毁的理由。在这里产生了一个闭包结构,局部变量的生看起来被延续了。

  • 现在我假设假设页面上有5个div 节点,我们通过循环来给每个div绑定onclick件,按照索引顺序,点击第1个div时弹出0,点击第2个div时弹出 1,以此类推。代码如下:

<html>
    <body>
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
    <script>
        var nodes = document.getElementsByTagName( 'div' );
        for( var i = 0, i < nodes.length; i++ ){
            nodes[ i ].onclick = function(){
            alert ( i );//5
        }
    };
    </script>
    </body>
</html>
  • 此时无论点击哪个div,最后弹出的结果都是5。这是因为div节点的onclick 事件是被异步触发的,当事件被触发的时候,for循环早已结束,此时变量i 的值已经是5,所以在div的onclick事件函数中顺着作用域链从内到外查找变量i时,查找的值总是5。

  • 在闭包的帮助下,把每次循环的 i 值都封闭起来。当在事件函数中顺着作用域链中从内到外查找变量 i 时,会先找到被封闭在闭包环境中的 i ,如果有5个 div ,里的 i 就分别是 0,1,2,3,4

    for ( var i = 0, i < nodes.length; i++ ){
          (function( i ){
              nodes[ i ].onclick = function(){
                 console.log(i);
              }
          })( i )
      };
    
  • 优点:

    • 作用域空间不销毁,所以变量也不会被销毁,增加了变量的声明周期
    • 在函数外部可以访问函数内部的变量
    • 保护私有变量,将变量定义在函数内,不会污染全局
  • 缺点:

    • 因为函数外部可以访问函数内部的变量,导致变量和内部的函数引用关系一直存在,内存不能销毁,会一直占用,使用量较大时会导致内存溢出
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值