分享

js 闭包

 碧海山城 2010-04-03
1、一个含有自由变量的函数; 
2、这些自由变量所在的环境。 
外部环境持有内部函数所使用的自由变量,对内部函数形成“闭包”,就这样。 
简单但不严格的说,一个函数的“自由变量”就是既不是参数也不是局部变量的变量。 

一个纯粹(无副作用)的函数如果不含有自由变量,那么每次用相同的参数调用后的得到的结果肯定是一样的。但如果一个函数含有自由变量,那么调用返回的结果不但依赖于参数的值,还依赖于自由变量的值。因此一个含有自由变量的函数要正确执行,必须保证其所依赖的外围环境的存在。 

基于类的面向对象程序语言中有一种情况,就是方法是用的自由变量是来自其所在的类的实例的。像这样: 
Java代码 
  1. class Foo {  
  2.     private int x;  
  3.     int AddWith( int y ) { return x + y; }  
  4. }  

这样的AddWith()有一个参数y和一个自由变量x,其返回的值既依赖于参数的值也依赖于自由变量的值。为了让AddWith()正确工作,它必须依附于Foo的一个实例,不然就得不到x的值了(称为:“变量i未与值相绑定”)。很好理解对吧。不过面向对象的语言里一般不把类称为闭包,没为什么,就是种习惯。 
当然严格来说方法所捕获的自由变量不是i,而是this;x是通过this来访问到的,完整写出应该是this.x。 

如果这个“外围环境”来自一个外围函数,并且内部函数可以作为返回值返回,那么外围函数的局部环境就不能在调用结束时就撤销。也就是说不能在栈上分配空间。 
Js代码 
  1. function AddWith(x) {  
  2.     return function(y) {  
  3.         return x + y  
  4.     }  
  5. }  

这样的内部函数有一个参数y和一个自由变量x。x在外围函数AddWith()里是一个参数,也就是一个“已绑定了值的变量”(bound variable)。AddWith()的局部作用域中含有内部函数所使用的自由变量,对内部函数形成闭包。为了让返回出去的内部函数能正常工作,这个内部函数必须依附于一个能提供x的值的环境,也就是AddWith()提供的闭包。这样我们就能够: 
Js代码 
  1. var addFive = AddWith(5)  
  2. var seven = addFive(2) // 2+5=7  


全局变量是一种特殊的自由变量。
 
 
 
 

收先先了解一下什么是闭包,


    闭包   是可以包含自由(未绑定)变量 的代码块;这些变量不是在这个代码块或者任何全局上下文中定义的,而是在定义代码块的环境中定义。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量的存在,相关变量引用没有释放)和为自由变量提供绑定的计算环境(作用域)。

    可能上面的定义有点晦涩,下面看一下《Python 核心编程》 对 闭包 的解释。

    如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被定义为闭包 。定义在外部函数内的但由内部函数引用或者使用的变量被称为自由变量 

 

下面是一个闭包的例子 (由于JAVA现在不支持闭包,这个闭包的例子是用Python写的,参见了《Python 核心编程》 

 

Python代码 
  1. def counter(start_at = 0):  
  2.     count = [start_at]  
  3.     def incr():  
  4.         count[0] += 1  
  5.         return count[0]  
  6.     return incr  

 

这里面count变量 就是一个 相对于函数incr 的自由变量(它在 函数incr 的外部作用域上,但又不在全局作用域上),内部函数incr 可以引用和使用这个变量。这个例子主要模拟一个计数器。

运行下面的代码

 

Java代码 
  1. count = counter(6)  
  2. print count()  
  3. print count()  

 

就会打印出

 

    7

    8

 

我们发现 内部函数(incr)不但可以引用其自身定义的变量,还可以引用外部函数(counter)定义的变量。

或者说 内部函数(闭包) 可以记忆状态, 它可以根据 它记忆的状态 来执行不同的操作。 而外部函数 负责

初始化状态(内部函数需要记忆的状态)。

 

那么为什么需要闭包,闭包的优势是什么呢?

我觉得就是可以记忆状态

但对象也可以记忆状态(通过对象的属性)。

那闭包和对象的区别是什么呢?

我觉得 就因为 闭包是函数 而不是对象。

 

我们会发现,如果用面向对象的方式来表达闭包

内部函数(闭包)就像 对象的方法

而外部函数  对象的构造器。

构造器 用来 初始化对象状态

而 对象的方法可以根据 对象的状态 来执行不同的操作。

 

 

好! 下面我们用面向对象的方式 创建一个 计数器(实现和上例一样的功能,用JAVA实现)。

 

Java代码 
  1. public class Counter {  
  2.     private int startAt;  
  3.     public Counter() {  
  4.         this(0);  
  5.     }  
  6.     public Counter(int startAt) {  
  7.         this.startAt = startAt;  
  8.     }  
  9.     public int incr(){  
  10.         return ++ this.startAt;  
  11.     }  
  12. }  

 

 

 运行Test类

 

Java代码 
  1. public class Test{  
  2.     public static void main(String[] args){  
  3.         Counter counter = new Counter(6);  
  4.         System.out.println(counter.incr());  
  5.         System.out.println(counter.incr());  
  6.     }  
  7. }  

 

会打印出(和上例打印输出相同)

 

    7

    8

 

 

那么JAVA(有了对象)还需要引入闭包吗?

我觉得不需要,

因为对象完全可以模拟 闭包 的行为,

而且 对象 才是 OOP 的 一级元素。

闭包 是 函数式编程(FP) 中的 概念,

 引入闭包 就相当于 引入 FP,

这只会破坏 JAVA 的 纯粹 与 简单 。。
 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多