標籤:ruby singleton
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的細緻差別