当我向Kernel
添加方法以使其全局可用时,我发现了一些奇怪的事情。这很有趣,我正在寻找一些文档或很好的解释。
让我们看一下代码:
档案:./ demo.rb
# example 1
module Kernel
def foo
puts "I'm defined inside the module!"
end
end
# example 2
module Bar
def bar
puts "I'm included! (bar)"
end
end
Kernel.send :include, Bar
# example 3
module Baz
def baz
puts "I'm included! (baz)"
end
end
module Kernel
include Baz
end
然后,在bash和IRB中
$ irb -r ./demo.rb
> foo
# I'm defined inside the module!
> bar
# NameError: undefined local variable or method `bar' for main:Object
> baz
# NameError: undefined local variable or method `baz' for main:Object
>
> self.class.ancestors
# => [Object, Kernel, BasicObject]
>
> include Kernel
>
> self.class.ancestors
# => [Object, Kernel, Baz, Bar, BasicObject]
>
> foo
# I'm defined inside the module!
> bar
# I'm included! (bar)
> baz
# I'm included! (baz)
foo
按预期工作,可用于包含Kernel
的所有对象。另一方面,bar
和baz
无法立即获得
我想这是因为IRB的评估上下文(Object
)已经包含Kernel
,并且在模块B中包含模块A将不会“重新加载”之前的所有内容B.
好的,它非常有意义,实际上重新包括Kernel
将添加另外两种方法。
然后,我的问题是:
Kernel
有效? (例1)答案 0 :(得分:1)
你在Ruby中调用foo.bar
会发生什么?像这样:
foo.class.ancestors.each do |klass|
if klass.public_instance_methods.include? :bar
return klass.instance_method(:bar).bind(foo).call
end
end
raise NameError
即。 Ruby搜索祖先以找到匹配的实例方法。
当你在Ruby中调用A.include B
时会发生什么?像这样:
B.ancestors.each do |mod|
A.ancestors << mod unless A.ancestors.include? mod
end
B
及其所有祖先都成为A
的祖先。这两种行为解释了一切:
Kernel
是有效的,因为它包含在Object
中,因此是每个对象的祖先,这意味着无论何时在任何对象上调用方法,都可以搜索其方法(包括新方法)。Kernel
的祖先只在被包含时被搜索,这是在你向它添加新祖先之前搜索的。