zoukankan      html  css  js  c++  java
  • 浅谈闭包

    1、什么是闭包?

      概念:有权访问另一个函数作用域内变量的函数都是闭包。

      闭包是通过改变JS回收机制保留某段作用域的一种手段。当一个函数执行完毕后,里面的局部变量是会被JS自带的垃圾回收机制给销毁的,从而释放内存。但是如果返回一个函数,而且函数里面有用到父级数声明的变量,那么此时,变量不会被回收,因为还有可能被用到,并且外界可以通过函数访问这段作用域下的变量。

      例一:

    funtion foo()
    {
      var a = "我是foo里的oo";  
      console.log(a);
    }
    
    foo();    //undefined

      这段代码执行后,其实a已经不存在了,因为foo执行完了之后foo的作用域消失了,所以作用域里面的变量不见了,被销毁了。在JS中存在自动垃圾回收,对不需要的变量或者没有使用的作用域进行定期清理。当 foo 执行完,函数内部的变量a没有在此作用域下任何地方再被引用,所以a变量的作用域会被JS内置垃圾收集器回收。

      那么,我们需要用某种方法来获得foo里面的a,怎么办呢?

      例二:

    function foo()
    {
       var a = "oo";
       return a;    
    }
    
    let b = foo();  //此时b就获得foo里面的a了。

      此时,a是不是就获得了呢?并没有,我们得到的只是a的副本,假设我以某种方法修改了a的值,b不会发生任何变化。所以,我们并不是使用的 foo 内部的a。既然直接返回a不行,那么我们试试返回一个函数。

      例三:

    function foo(c)
    {
      var num = c;
      return function A()
        {
           num++;
           return num;          
        }  
    }
    
    var b = foo(5);
    
    b();   //6    
    b();   //7

      是不是很奇怪,按道理每次都会返回6呀,怎么会每次叠加呢。其实不然,我们执行的是函数b(从foo返回出来的),并没有重新执行foo,所以也就不会每次给num重新赋值5。至于为什么会变成这种累加的情况呢,这是因为函数foo执行完后,其内部的的A函数里面对num有引用,所以foo的作用域以及变量a被保留在了函数A中,返回给了b。

      现在,我们就能在外界通过函数b来访问foo内部的变量num了。这就是闭包,我们通过返回一个函数打通了函数内部与外界的桥梁。

    2、闭包的作用

      闭包可以解决的一个典型的问题就是循环绑定的问题,由于var声明的变量可以穿透作用域,所以如下的代码会出现问题。

      例四:

    <ul class="list">
          <li class="bloc">1</li>
          <li class="bloc">2</li>
          <li class="bloc">3</li>
          <li class="bloc">4</li>
          <li class="bloc">5</li>
     </ul>
      var ali = document.querySelectorAll('.wrap ul li')
      for(var i = 0,l = ali.length;i < l;i++){
       ali[i].onclick = function(){
            console.log(i)  //5 5 5 5 5
        }
      }

      执行上面代码你会发现,当你点击任意li时,都会打印出5。这不对呀,和我们想的不一样呀,按道理点击第几个 li 就会打印出相应的下标呀。这是因为,我们绑定了五次onclik事件,无论你有没有触发后面的的函数,for循环都会执行,当你点击的时候,for循环已经执行完了,i 的值早已经变成了5。var 声明的i能够穿透作用域,每次触发点击事件时,函数内部的 i 也都是5。

      那么,我们是不是应该想办法把每次循环时的 i 值保存下来呢,就好像每个点击事件都重新给一个新的 i 值。于是,通过闭包可以保存每次循环的 i 值,请看例子

      例五:

    for(var i=0;i<ali.length;i++)
    {    
       ali[i].onclick=(function(j){
           return function(){
              console.log(j);
          }
        })(i)  
    }    

      我们通过立即执行函数后返回了一个函数的形式,把每次循环的 i 值通过传参放到了不同的函数中,每个函数都是一个独立的作用域,每个函数都有了各自的i值,互相不影响,现在就如我们期望的一样了,点击第几个li打印出第几个 li 的下标。

      说点题外话,我们使用es6的let也可以实现上面功能

      例六:

    for(let i=0;i<ali.length;i++){
       ali[i].onclick = function(){
         console.log(i);
        } 
    } 

      因为let声明的是局部变量,只在当前作用域内有效,es6新增的块级区域即是由{}可以区分开,而var不区分块级区域的,所以后面var的i可以改变前面的i,而let声明的i只作用于当前区域,所以并不影响之前的i,即每个块级区域都相当于一个独立的变量,所以最后每个按钮打印出的值都是对应的值。

  • 相关阅读:
    3、MHC主要组织相容性复合体
    2、抗原
    1、免疫细胞
    【转】python3 内循环中遍历map,遍历一遍后再次进入内循环,map为空
    【转】Map 与 Unordered_map
    Chapter7 抑癌基因
    总结 搜索和最短路径问题
    1131 Subway Map DFS解法 BFS回溯!
    python 报错信息汇总
    python str转换成timedelta或date
  • 原文地址:https://www.cnblogs.com/tg666/p/12068926.html
Copyright © 2011-2022 走看看