首页 > 代码库 > ruby的def 和 define_method的细致差别
ruby的def 和 define_method的细致差别
ruby语言中,class_eval和instance_eval的具体差别比较明显, class_eval针对的是一个Class的对象,然后在此对象中可以定义instance方法。而instance_eval是针对某个对象,打开的class是 eigenclass或者singleton class。
class A
end
A.instance_eval do
def hello
puts "hello"
end
end
A.class_eval do
def world
puts "world"
end
end
此时,我们定义的是A的class方法,也就是说我们在A.eigenclass中定义了一个实例方法(instance method)"hello".
A.hello # hello
A.world # NoMethodError: undefined method `world‘ for A:Class
A.new.world # hello
到这一点为止,大家都感觉比较好明白。
现在下面的问题来了,
A.instance_eval do
define_method(:t1) do
puts "t1"
end
end
按照很多人说法,define_method和def机制类似的,只是def更高效,define_method是动态定义方法而已,并且define_method可以使用局部的变量。
所以,我们从上面分析,方法:t1 应该还是A的eigenclass的实例方法,或者说A的类方法。
A.t1 应该输出 "t1"
但实际是:
NoMethodError: undefined method `t1‘ for A:Class
A.new.t1 # t1所以在instance_eval下,define_method 和 class_eval 是一致的,都是定义了A得实例方法。
而如果我们这样写,
class <<A
define_method(:t2) do
puts "t2"
end
end
A.t2 # t2上面这种方法确实是在A.eigenclass中定义了实例方法 t2。
在这篇文章开头,我们说过,A.instance_eval 确实打开的时eigenclass, 为什么对def是正确地,而对define_method是不对的呢? 而在class << A 这种显示打开eigenclass时, define_method也是符合逻辑的。
或许是为了方便写meta方法,做了这种有意的调整。
klass.instance_evaldo
method_object = instance_method(method)
define_method(method)do |*args, &block|
puts "==> calling #{method} with #{args.inspect}"
result = method_object.bind(self).call(*args, &block)
puts "<== #{method} returned #{result.inspect}"
result
end
end
在上面这段代码里,对klass做了定义方法,而大部分这种写代码而言,动态生成方法都是想生成实例方法,而不是klass的类方法,所以,define_method在这个时候,做了特殊的变化,从而把我们先前总结的规律给引入了一个特殊的例外。如果不引入特殊的例外,这时,必须再加一个包装层, klass.class_eval do .... end 来做处理。
ruby的def 和 define_method的细致差别