闭包的概念和闭包与lambda表达式的关系

来源:互联网 发布:好听的云南网络山歌 编辑:程序博客网 时间:2024/06/05 16:25

一。Javascript中闭包:

1.变量的作用域

  要理解闭包,首先必须理解Javascript特殊的变量作用域。
  变量的作用域无非就是两种:全局变量和局部变量。
  
  Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 
[javascript] view plain copy
  1. var n=999;  
  2. function f1(){  
  3.   alert(n);  
  4. }  
  5.  f1(); // 999  

  另一方面,在函数外部自然无法读取函数内的局部变量。 
[javascript] view plain copy
  1. function f1(){  
  2.   var n=999;  
  3. }  
  4.  alert(n); // error  

  这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量! 
[javascript] view plain copy
  1. function f1(){  
  2.   n=999;  
  3. }  
  4.  f1();  
  5.  alert(n); // 999  

2.如何从外部读取局部变量

  出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
  那就是在函数的内部,再定义一个函数。
[javascript] view plain copy
  1. function f1(){  
  2.   n=999;  
  3.   function f2(){  
  4.     alert(n); // 999  
  5.   }  

  既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了嘛!
[javascript] view plain copy
  1.  function f1(){  
  2.    n=999;  
  3.    function f2(){  
  4.      alert(n);  
  5.   }  
  6.   return f2;  
  7. }  
  8. var result=f1();  
  9. result(); // 999  

3.JS闭包的概念

  上面的f2函数,就是闭包。闭包其实就是定义在一个函数内部的函数(因为是子函数所以能够读取所在父函数的内部变量)。
  在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

4.闭包的用途

  1.可以读取函数内部的变量.
  2.让这些变量(闭包引用的变量)的值始终保持在内存中。
[javascript] view plain copy
  1.  function f1(){  
  2.    var n=999;  
  3.    nAdd=function(){n+=1}  
  4.    function f2(){  
  5.      alert(n);  
  6.    }  
  7.    return f2;  
  8. }  
  9. var result=f1();  
  10. result(); // 999  
  11. nAdd();  
  12. result(); // 1000  
        在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
        为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
        这段代码中另一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此 nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个

       匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。


5.使用闭包的注意点

  1.由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包

  2.闭包会在父函数外部,改变父函数内部变量的值。


二。Java中的闭包

1.java中闭包的定义

  定义:闭包能够将一个方法作为一个变量去存储,这个方法有能力去访问所在类的自由变量。

  Java8 语言之前本身还没有正式支持闭包,但它却允许模拟闭包(内部类+接口)。可以使用匿名的内部类来实现闭包。

  如何让这个普通对象能够访问所在类的自由变量?内部类。内部类能够访问外部类的所有属性及方法。

  隐藏具体实现是内部类的作用之一,如何保证隐藏具体实现的同时还能将闭包传递到外部使用?让内部类实现通用接口,然后将内部类对象向上转型为接口类型。

2.代码简单实现:

[java] view plain copy
  1.     public final static String name = "纯牛奶";// 名称  
  2.   
  3.   
  4.     private static int num = 16;// 数量  
  5.   
  6.   
  7.     public Milk() {  
  8.         System.out.println(name + ":16/每箱");  
  9.     }  
  10.   
  11.   
  12.     /** 
  13.      * 闭包 
  14.      *  
  15.      * @return 返回一个喝牛奶的动作 
  16.      */  
  17.     public Active HaveMeals() {  
  18.         return new Active() {  
  19.             public void drink() {  
  20.                 if (num == 0) {  
  21.                     System.out.println("木有了,都被你丫喝完了.");  
  22.                     return;  
  23.                 }  
  24.                 num--;  
  25.                 System.out.println("喝掉一瓶牛奶");  
  26.             }  
  27.         };  
  28.     }  
  29.   
  30.   
  31.     /** 
  32.      * 获取剩余数量 
  33.      */  
  34.     public void currentNum() {  
  35.         System.out.println(name + "剩余:" + num);  
  36.     }  
  37. }  
  38.   
  39.   
  40. /** 
  41.  * 通用接口 
  42.  */  
  43. interface Active {  
  44.     void drink();  
  45. }    
  46.   
  47. //闭包的使用  
  48. public class Person {     
  49.     public static void main(String[] args) {    
  50.         //买一箱牛奶    
  51.         Milk m = new Milk();   
  52.         Active haveMeals = m.HaveMeals();  
  53.         //没事喝一瓶    
  54.         haveMeals.drink();    
  55.         //有事喝一瓶    
  56.         haveMeals.drink();   
  57.         //看看还剩多少?    
  58.         m.currentNum();    
  59.     }     
  60. }    


三、闭包与lambda表达式
你可能已经听说过闭包(closure,不要和Clojure编程语言混淆)这个词,你可能会想Lambda是否满足闭包的定义。用科学的说法来说,闭包就是一个函数的实例/变量,且它可以无限制地访问那个函数的非本地变量。例如,闭包可以作为参数传递给另一个函数。它也可以访问和修改其作用域之外的变量。现在,Java 8的Lambda和匿名类可以做类似于闭包的事情:它们可以作为参数传递给方法,并且可以访问其作用域之外的变量。但有一个限制:它们不能修改定义Lambda的方法的局部变量的内容。这些变量必须是隐式最终的。

java8 支持闭包。和js不同的是 java8的闭包包内不支持改变外部变量。





原创粉丝点击