Ruby 常量查找

对 Ruby 中常量查找只有基础的认识,使用上还是有不少疑问,比如 “::A” 的含义、为什么有时使用 “A::B” 而有时直接用 “B”。
花时间查了资料来加深对此的理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module Record
def self.method
puts 'outer'
end
module Music
Record.method # outer
module Record
def self.method
puts 'inner'
end
end
Record.method # inner
::Record.method # outer
Music::Record.method # inner
end
end

我们从简单的例子出发。在上文的代码中,共存在着两个名为 Record 的 module。一个是在最外层,一个是在 Music 内部。
当第 9 行调用 Record.method 时,由于 Music 内的 Record 还未被定义,因此调用的只能是最外层的 Record module。
而在第 17 行,当我们重新调用时,却发现调用的已经是内部的 Record 了。
可见常量的调用时是有一个先后顺序的,简单的说就是从近及远,先使用近的。虽然我们从直观上很容易理解近的含义,但严格上的顺序是通过 Module.nesting 得到的。在 Music 内部调用得到 [Record::Music, Record],可见 Record::Music 确实比 Record 有更高的优先级。

我们再来看看 ::Record.method,在使用 :: 之后,调用的就变成了外层的 Record 。::的作用是从 top level 来查找相关常量,也就是调用最外层的 Record。

Record.methodMusic::Record.method 有什么区别吗?
若都能找到相同的对象,这两者是没有区别的,如 16、18 行。
但在 top level 中调用 Music::Record.method 时,需使用完整的路径 Record::Music::Record.method,不然会报 uninitialized constant

总结

Module.nesting 只是查找常量的一部分,更完整的常量查找步骤如下:

  1. 向外找,从 Module.nesting 依次查找
  2. 向上找,从打开类 的 ancestors 依次查找(打开类:Module.nesting.first,若为空则为 Object)
    Everything you ever wanted to know about constant lookup in Ruby 讲得非常详情,就不多述了。

参考