zoukankan      html  css  js  c++  java
  • JavaScript闭包

    写一下闭包到底是个什么东西,看了无数的博客,无数的例子,基本上还没有看到用简单的方式来说明闭包,我试着写写,没有新的东西,基本都是前辈资料的总结。抛个砖先。热烈欢迎大神们斧正。


    按照惯例,先看下官方是如何定义的

    Closures are functions that refer to independent (free) variables.
    In other words, the function defined in the closure 'remembers' the environment in which it was created in.

    • 第一句:闭包是函数,什么样的函数?涉及到独立变量的函数。什么叫“独立变量”?我也不明白,一会边写边说。
    • 第二句:换言之,这个函数是在闭包的‘记忆’环境定义。“记忆环境”是什么东东?还是不明白。

    好吧,以我苦逼的英语水平,就只能这样理解了。但这说了什么?我不明白,一点都不明白。
    链接在这里,英语好的少年可以去看看。


    再看另一个定义

    A "closure" is an expression (typically a function) that can have free variables together with an environment
    that binds those variables (that "closes" the expression)

    http://jibbering.com/faq/notes/closures/

    闭包是一个表达式(通常是一个函数),什么样的表达式?拥用自由变量和一个绑定这些变量的环境的表达式。
    这个定义相对要明白一些,但还是不太具体。


    再看看wiki

    In programming languages, a closure (also lexical closure or function closure) is a function or reference to a function together with a referencing environment—a table storing a reference to each of the non-local variables (also called free variables or upvalues) of that function.A closure—unlike a plain function pointer-allows a function to access those non-local variables even when invoked outside its immediate lexical scope.

    http://en.wikipedia.org/wiki/Closure_(computer_programming)

    好吧,又是乱七八糟的一大堆,但是最后一句很关键:闭包不像一般函数,它允许一个函数访问这些变量,即使这些引用超出了变量的作用哉。

    诸位不要觉得看定义是很烦人,是没有必要的。在我看来这是非常非常重要的东西。因为大多知识点,在定义上面都写得很清楚。不过closures算一个例外吧,因为它本来就很难说清楚......
    既然国外文献定义得不太明白,就看看国内的先辈们有没有好的定义,摘录如下:

    1. 闭包就是能够读取其他函数内部变量的函数。
    2. 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。
    3. 闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。
    4. 闭包是定义在另一个函数中的匿名函数。(javascript详解第二版 ——Ellie Quigley Page 124)

    对于第3条,是来自百度百科,看了之后我只能说:壮哉我大百度!还有比这更详尽的定义么?有发现的请发给我!这里对自由变量有个解释:未绑定到特定对象的变量。


    大部份程序员由于逼格原因,是鄙视百度的,动辙google,跳辙wiki。殊不知知识莫问出处,好用就行。

    这里多说两句,除了一些新的知识,国内论坛基本都有非常详尽的资料,如果你没有达到国内的资料已经不有满足你的程度,就不要到处找英文资料,因为母语资料理解起来,肯定比英文快得多。

    嗯,直白点说就是:在程序这一块,英语是用来用的,而不是用来show的。


    所以,我们总结一下什么是闭包:

    • 什么时候需要用到闭包:

      • 需要在一个函数外部,访问函数内部的变量的时候(也就是说在函数运行完之后,你想要把变量保存下来,待需要的时候调用。而不是通过垃圾回收机制消除(garbage collection))。
      • 保护变量安全。一个函数的内部变量,只能内部函数引用。
    • 如何定义闭包:

      • 在一个函数内部,定义一个函数,并返回一个函数的引用。

    如何在外部引用一个函数内部的变量?

    function a(){
     var i = 1;
    }
    alert(i); //undifined
    // 为什么这样?因为一个变量的作用域只在一个函数本身,这是javascript最基本的入门的概念,想来不用多说。
    function a(){
      var i=0;
      function b(){
        alert(++i); 
      }
      return b;
    }
    var c = a();
    c(); //1; 
    

    这个例子来自于百度百科,有三个关键点

    1. b是定义在a的内部
    2. a的运行结果是 return b。此时b就一直保存在内存当中,直到你手动删除为止。
    3. b引用了a的变量(这一点极其重要)。

    所以,c = a();其实调用的就是b,由于b是在a的内部,所以就可以访问i(这是javascript的链接作用域(chain scope),内部函数可以访问外部函数的变量)。
    这样,就达到了在a的外部,调用a中的变量。

    再有,a中定义的函数是b,而不是一个匿名函数,但是这样是可行的。所以,上面第4条的定义就是错的,闭包并不一定是一个匿名函数。

    这一个例子,基本就已经说完了闭包了。但是看的人可能还是没有一个明确的概念,那么就继续看几个例子。

    function a(){
      var i = 1;
      function b(){
        alert(i);
      }
      i++;
      return b;
    }
    var f = a();
    f(); // 2
    

    为什么呢是2呢?
    return b是在a的最后执行,所以,在ruturn b的时候,已经将a中代码全部执行了,所以 i=2;若在i++前返回b。
    f()的结果就是1。这就可以推出:也可以把 var i = 1;写在function b的后面。只要写在return之前即可。

    function a(){
      var i = 1;
      b = function(){alert(i)}
      c = function(){i++}
      d = function(j){i = j}
    }
    a(); //运行一次a,为bcd赋值。
    b(); // 1
    c(); // i++
    b(); // 2
    d(3); // i = 3
    b(); //3
    
    • 此处应有三点需要说明:
    1. 在函数内部定义变量一定要用var,否则定义的就是一个全局变量。在这里b c d皆没有用var,所以就可以在外部直接调用这三个全局变量
    2. a需要运行一次,才会给bcd赋值。否则会报错 b c d未定义
    3. b c d 都在a内,所以都可以调用变量i.

    循环中的闭包

    function closureInLoop(Ar){
      var result = [];
      for(var i=0;i<Ar.length;i++){
        var item = 'item' + Ar[i];
        result.push(function(){alert(item + ' ' + Ar[i])});
    } 
      return result; 
    }
    // 
    function test(){
      var fnList = closureInLoop([1,2,3]);
      alert(fnList);
      for(var j=0;j<fnList.length;j++){
        fnList[j]();
      }
    }
    test(); // item3 undifined(3次);
    

    我们原以为,会依然出现item 1,item 2,item 3。结果却不同,这是什么原因?
    先把fnList输出来看看
    [fnction(){alert(item + ' ' + Ar[i])},function(){alert(item + ' ' + Ar[i])},function(){alert(item + ' ' + Ar[i])},]
    三个一模一样的function,都是调用的Ar[i]。此时i是多少呢?看下前面的closureInLoop的for循环。
    传入的参数是[1,2,3],所以closureInLoop中的Ar.length就是3。所以for执行完的时候:
    i=3;
    item = 'item' + Ar[2];也就是item3;

    由于return result了。所以此时resulte里面的function就形成了闭包。
    三个function都引用了i和item。此时i=3,item = item3;
    所以都是function(){item + ' ' + Ar[3]}
    但是Ar传入的是[1,2,3],Ar.length = 3 没错。
    但是Ar[0] = 1;Ar[1] = 2; Ar[2] = 3....都没错。

    但是骚年们,Ar[3] = undifined;
    alert(item + ' ' + Ar[3]) = item3 undifined。

    Over,这个太麻烦,我想也没几个人这样写吧。不过这个例子非常经典,可以仔细看看。

    例外一种循环里的闭包

    for(var i=0;i<10;i++){
      setTimeout(function(){
        console.log(i);
      },1000);
    }
    // 这样并不能依次输出i,而是输出10次10。
    

    每次都创建一个新的闭包

    function createClosure(number,reference){
      var num = number,
      ref = reference,
      anArray = [1,2,3];
      return function(x){
        num += x;
        anArray.push(num);
        alert(num + ' ' + anArray.toString() + ' ' + ref);
      }
    }
    closure_1 = createClosure(20,'ref_1');
    closure_1(10); //30 [1,2,3,30] ref_1
    // 
    closure_2 = createClosure(100,'ref_2');
    closure_2(-10); //90 [1,2,3,90] ref_2
    

    说好的闭包呢,为什么没有继续用closure_1的nunber和reference?
    因为我们每建立一个新的闭包,都重新将number和reference通过参数改变。


    参考资料
    http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
    http://jibbering.com/faq/notes/closures/
    http://en.wikipedia.org/wiki/Closure_(computer_programming)
    http://baike.baidu.com/view/648413.htm
    http://coolshell.cn/articles/6731.html
    http://bonsaiden.github.io/JavaScript-Garden/zh/

  • 相关阅读:
    如何将网格式报表打印成其它样式
    拥有与实力不相称的脾气是种灾难——北漂18年(23)
    8.8.1 Optimizing Queries with EXPLAIN
    mysql 没有rowid 怎么实现根据rowid回表呢?
    secondary index
    8.5.5 Bulk Data Loading for InnoDB Tables 批量数据加载
    mysql 中key 指的是索引
    8.5.4 Optimizing InnoDB Redo Logging 优化InnoDB Redo 日志
    8.5.3 Optimizing InnoDB Read-Only Transactions 优化InnoDB 只读事务
    8.5.1 Optimizing Storage Layout for InnoDB Tables InnoDB表的存储布局优化
  • 原文地址:https://www.cnblogs.com/CoinXu/p/4574899.html
Copyright © 2011-2022 走看看