原文:https://cirw.in/blog/constant-lookup 这是一篇翻译,在此给大家分享,是因为我觉得讲的太好。不知道是不是因为我读的相关书籍太少,还是从来没有 google 过相关知识点,最近看到这篇文章很激动。 它讲的是关于 Ruby 中 Constant lookup 的东西,仔细读了三遍之后,我算是基本了解了 Ruby 的 constant lookup 机制。
在 Ruby 中,当你需要访问一个常量的时候,很简单直接使用这个常量的名字就行。比如:People
但是你知道 Ruby 是怎么去找到People
的吗?
简单讲他会去三个地方寻找:
Module.nesting
Module.nesting.first.ancestors
Object.ancestors
(条件:当Module.nesting.first.nil?
为 true,或者你正处于一个 module 中)
注意: 前两个是没有什么条件的我们首先需要知道这是什么,先来一段代码
module A
module B; end
module C
module D
puts Module.nesting
B == A::B
end
end
end
#A::C::D
#A::C
#A
#=> true
从puts
的结果来看,你应该能很直观的了解到什么是Module.nesting
。但是我想这里可能会产生一个疑问,为什么到了A
就没有了?
首先说明我执行这段代码是在普通irb
环境,所以相当于是Object
的一个实例中。所以A
再往上走,就应该到了Object
。
如果你采用 module 的快捷定义形式,Module.nesting
会跳过对应的 namespace
module A
module B; end
end
module A::C
B
end
# NameError: uninitialized constant A::C::B
当 Ruby 在Module.nesting
中找不到对应的常量的时候,它会在当前打开的 class 或者 module 的 ancestors 中去寻找。
class A
module B; end
end
class C < A
B == A::B
end
这里,当前打开 class 或者 module 的概念有点让人迷惑,根据例子来看,我的理解是你访问常量的代码定义所在的 class 或者 module
class A
def get_c; C; end
end
class B < A
module C; end
end
B.new.get_c
# NameError: uninitialized constant A::C
当你发现Module.nesting == []
的时候,说明你正处于 top level,也就是说你正处于打开的Object
中。根据文章开始列出的三个搜寻地点,第三个Object.ancestors
满足了搜寻条件,所以 Ruby 会去Object.ancestors
中寻找。
关于Object
这里有个重要的假设,如果你当前打开的是一个 module,那么 Ruby 会自动将Object.ancestors
加入到你搜寻 list 中。
module A; end
module B;
A == Object::A
end
根据这个假设,以及文章开始列出的搜寻地址 2,我们可以得出一个结论:那就是 top level 的 constant 任何时候都是 accessable 的。
一般情况下,class_eval 以及它的 block 并不会对 constant lookup 产生什么影响。Ruby 还是会从 block 定义的地方开始去搜寻。
class A
module B; end
end
class C
module B; end
A.class_eval{ B } == C::B
end
然而,当你在 block 中使用的是字符串时,这个字符串执行时对应的Module.nestign
将只包含这个 class 本身。
class A
module B; end
end
class C
module B; end
A.class_eval("B") == A::B
end
因为单间类的 ancestors 并不包含类本身,所以
class A
module B; end
end
class << A
B
end
# NameError: uninitialized constant Class::B
class A
module B; end
end
class << A; ancestors; end
[Class, Module, Object, Kernel, BasicObject]
欢迎补充