我在给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
)的 evaluation context 已经包含了Kernel
,而在模块B中包含模块A将不会“重新加载”之前包含的所有B。
好吧,这是完全合理的,实际上重新包含Kernel
将添加其他两个方法。
那么,我的问题是:
1.为什么 openingKernel
工作?(示例1)
1.如果 * 打开 * 模块的处理方式不同,为什么第三个例子不能正常工作?
1条答案
按热度按时间t3psigkw1#
当你在Ruby中调用
foo.bar
时会发生什么?类似这样:字符串
也就是说,Ruby会搜索所有的祖先来找到匹配的示例方法。
当你在Ruby中调用
A.include B
时会发生什么呢?类似这样:型
B
及其所有祖先都成为A
的祖先。这两个行为解释了一切:1.打开
Kernel
是可行的,因为它包含在Object
中,因此是每个对象的祖先,这意味着它的方法(包括新方法)可以在任何对象上调用方法时搜索。1.打开一个模块并没有区别对待。你的第二个和第三个例子实际上是一样的。它们都不起作用,因为
Kernel
的祖先只在它被包含时才被搜索,这是在你向它添加新的祖先之前。