我会使用
Kernel#set_trace_func
方法来解释底层发生了什么。首先看下面的代码和输出:
trace = lambda do |event,file,line,id,binding,klass|
p [event,File.basename(file),line,id,binding,klass]
end
set_trace_func trace
class MyClass
def self.bar;end
def one
def two
end
end
end
obj = MyClass.new
obj.one
obj.instance_eval do
def three
end
end
输出:
-----------------
----------------
-----------------
-----------------
-----------------
-----------------
["c-call", "test.rb", 9, :singleton_method_added, #<Binding:0x83ab2b0>, BasicObject]
["c-return", "test.rb", 9, :singleton_method_added, #<Binding:0x83aaeb4>, BasicObject]
["line", "test.rb", 10, nil, #<Binding:0x83aab80>, nil]
["c-call", "test.rb", 10, :method_added, #<Binding:0x83aa900>, Module]
["c-return", "test.rb", 10, :method_added, #<Binding:0x83aa07c>, Module]
-----------------------------
["line", "test.rb", 16, nil, #<Binding:0x83a976c>, nil]
["c-call", "test.rb", 16, :new, #<Binding:0x83a9488>, Class]
["c-call", "test.rb", 16, :initialize, #<Binding:0x83a90a0>, BasicObject]
["c-return", "test.rb", 16, :initialize, #<Binding:0x83a8e20>, BasicObject]
["c-return", "test.rb", 16, :new, #<Binding:0x83a8b28>, Class]
---------------------------
---------------------------
---------------------------
["c-call", "test.rb", 11, :method_added, #<Binding:0x83a7de0>, Module]
["c-return", "test.rb", 11, :method_added, #<Binding:0x83a79f8>, Module]
---------------------------
["line", "test.rb", 18, nil, #<Binding:0x83a7034>, nil]
["c-call", "test.rb", 18, :instance_eval, #<Binding:0x83a6c10>, BasicObject]
["line", "test.rb", 19, nil, #<Binding:0x83a65f8>, nil]
["c-call", "test.rb", 19, :singleton_method_added, #<Binding:0x83a61d4>, BasicObject]
["c-return", "test.rb", 19, :singleton_method_added, #<Binding:0x83a5ef0>, BasicObject]
["c-return", "test.rb", 18, :instance_eval, #<Binding:0x83a5d4c>, BasicObject]
解释:
看一下部分A下面的5行。它简单地告诉我们,当Ruby在类中找到def
关键字时,它会通过调用钩子方法Module#method_added
将该方法添加为该类的实例方法。相同的解释适用于部分C下面的两行。
现在,在obj.instance_eval {..}
中发生了什么?
如果您查看下面D部分的行,就可以清楚地了解这一点。从最后一行开始和第二行。在instance_eval
块内,def third
会调用钩子方法BasicObject#singleton_method_added
,将third
作为ob
对象的singleton_method
添加进去。
这就是MRI的编写方式。
def self.two; end
会像 instance_eval 一样运行。 - fl00r