详解Ruby中的instance_eval方法及其与class_eval的对比
instance_eval方法
这个BasicObject#instance_eval有点类似JS中的bind方法,不同的时,bind是将this传入到对象中,而instance_eval则是将代码块(上下文探针ContextProbe)传入到指定的对象中,一个是传对象,一个是传执行体。通过这种方式就可以在instance_eval中的代码块里访问到调用者对象中的变量。
示例代码
classMyClass definitialize @v=1 end end obj=MyClass.new obj.instance_evaldo self#=>#<MyClass:0x33333@v=1> @v#=>1 end v=2 obj.instance_eval{@v=v} obj.instance_eval{@v}#=>2
此外,instance_eval方法还有一个双胞胎兄弟:instance_exec方法。相比前者后者更加灵活,允许对代码块传入参数。
示例代码
classC definitialize @x=1 end end classD deftwisted_method @y=2 #C.new.instance_eval{“@x:#{@x},@y>:#{y}”} C.new.instance_exec(@y){|y|“@x:#{@x},@y:#{y}”} end end #D.new.twisted_method#=>“@x:1,@y:” D.new.twisted_method#=>“@x:1,@y:2”
因为调用instance_eval后,将调用者作为了当前的self,所以作用域更换到了classC中,之前的作用域就不生效了。这时如果还想访问到之前@y变量,就需要通过参数打包上@y一起随instance_eval转义,但因为instance_eval不能携带参数,所以使用其同胞兄弟instance_exec方法。
instance_eval与class_eval的区别
###instance_eval
首先从名字可以得到的信息是,instance_eval的调用者receiver必须是一个实例instance,而在instance_evalblock的内部,self即为receiver实例本身。
obj_instance.instance_evaldo self#=>obj_instance #currentclass=>obj_instance'ssingletonclass end <!--more-->
根据这个定义,如果在一个实例上调用了instance_eval,就可以在其中定义该实例的单态函数singleton_method
classA end a=A.new a.instance_evaldo self#=>a #currentclass=>a'ssingletonclass defmethod1 puts'thisisasingletonmethodofinstancea' end end a.method1 #=>thisisasingletonmethodofinstancea b=A.new b.method1 #=>NoMethodError:undefinedmethod`method1'for#<A:0x10043ff70>
同样,因为类class本身也是Class类的一个实例,instance_eval也可以用在类上,这个时候就可以在其中定义该类的singleton_method,即为该类的类函数。
换句话说,可以用instance_eval来定义类函数classmethod,这比较容易混淆,需要搞清楚。
classA end A.instance_evaldo self#=>A #currentclass=>A'ssingletonclass defmethod1 puts'thisisasingletonmethodofclassA' end end A.method1 #=>thisisasingletonmethodofclassA class_eval
###class_eval
再来看class_eval,首先从名字可以得到的信息是,class_eval的调用者receiver必须是一个类,而在class_evalblock的内部,self即为receiver类本身。
classA end A.class_evaldo self#=>A #currentclass=>A end
根据这个定义,如果在一个类上调用了class_eval,就可以在其中定义该类的实例函数instance_method
classA end a=A.new a.method1 #=>NoMethodError:undefinedmethod`method1'for#<A:0x10043ff70> A.class_evaldo self#=>A #currentclass=>A defmethod1 puts'thisisainstancemethodofclassA' end end a.method1 #=>thisisainstancemethodofclassA
换句话说,可以用class_eval来定义实例函数instancemethod,这也比较容易混淆,需要搞清楚。