闭包这个概念,我最早是从javascript中了解到的。这个概念逐渐被所有的主流语言所接受(包括lambda)。而ruby中的闭包概念也差不太多,那么就先从ruby的代码块说起
什么是代码块
我觉得可以这样理解,在Ruby中有一种方法A,在定义的时候,我们不能准确地说出它的方法逻辑是什么(这个方法虽然有明确的目的,即会用来做什么,但具体工作要视具体的环境而定,因此这种方法可以尽可能地被复用到各种场合),当我们使用时,就会传递给它一些需要执行的逻辑,这个逻辑就是“块”。我觉得类似java、c++中的匿名函数Lambda,只是更简单、更好用了。
1 | def a_method(a,b) |
什么是闭包
块是Ruby实现闭包的一种手段,闭包是函数式编程中的一个重要概念。当定义一个块时,它会获取当前运行环境的“绑定”(可以理解为上下文信息),获得“绑定”的块是完整的,它们以一个“整体”传递给方法。比如这样:
1 | def a_method |
变量x作为块的绑定一起进入了方法中,并且即便方法a_method中也有一个x的局部变量,但这不会影响块中的x,因为块的绑定是在开始就确定的,并且一直带着的,这就是闭包的特性。
闭包强调了作用域的概念,在Ruby中class、module和def的出现,意味着作用域的切换,每当进入一个新的作用域,前一个作用域的绑定就会丢失,新作用域会重新确定当前的绑定。闭包就是给与了穿越作用域门的可能(class、module、def)。简单的办法就是用Class.new代替class,使用Module.new代替module,用Module#define_method代替def,并使用到前文所介绍的块(代码与变量的整体)。这里有很多概念,例如扁平作用域、共享作用域……(慢慢消化)
instance_eval()
Object#instance_eval,这个方法特殊之处在于
1 | class Myclass |
没错,“irb(main):036:1> @v # =>1表示块的接受者obj成为self,所以才能访问到obj的私有方法和实例变量,而最后的几句告诉我们,instance_eval还能改变self对象,也就是说Ruby打破了封装。所以很多时候,写ruby代码得小心谨慎,一不留神搞出个bug,让你找都找不到。