2016-06-28 65 views
4

我最近發現Ruby(2.2.1)有一些「有趣」的行爲。Ruby的Object#const_get是如何工作的?

module Foo 
    class Foo 
    end 
    class Bar 
    end 
end 

Foo.const_get('Foo') #=> Foo::Foo 
Foo.const_get('Bar') #=> Foo::Bar 
Foo.const_get('Foo::Foo') #=> Foo 
Foo.const_get('Foo::Bar') #=> NameError: uninitialized constant Foo::Foo::Bar 
Foo.const_get('Foo::Foo::Bar') #=> Foo::Bar 
Foo.const_get('Foo::Foo::Foo::Bar') #=> NameError: uninitialized constant Foo::Foo::Bar 
Foo.const_get('Foo::Foo::Foo::Foo::Bar') #=> Foo::Bar 
Foo.const_get('Foo::Foo::Foo') #=> Foo::Foo 
Foo.const_get('Foo::Foo::Foo::Foo') #=> Foo 
Foo.const_get('Foo::Foo::Foo::Foo::Foo') #=> Foo::Foo 
Foo.const_get('Foo::Foo::Foo::Foo::Foo::Foo') #=> Foo 

這有點令人驚訝。我的理解是,const_get首先在接收器的常量集合中查找常量,然後查看Object的常量。好的。爲什麼第四個Foo#const_get失敗,第三個不?

我也很好奇爲什麼調用Foo#const_get模塊和類之間的交替取決於你添加多少::Foo

+0

你可以檢查你的第三個例子('Foo.const_get('Foo :: Foo')')?我只得到'Foo',而不是'Foo :: Foo'。 – matt

+0

@matt你是對的,它只是返回'Foo'。編輯原始問題。 – Huliax

回答

5

The docs say

這種方法將遞歸查詢常量名如果提供一個命名空間的類名。

所以Foo.const_get('Foo::Bar')基本上與Foo.const_get('Foo').const_get('Bar')相同。使用這種解釋你的結果是有道理的。

你的第三個例子:

Foo.const_get('Foo::Foo') 

相同

Foo.const_get('Foo').const_get('Foo') 

第一const_get看起來在頂層Foo(模塊)內定義的常量,並且發現嵌套類。所以整個事情實際上就變成:

Foo::Foo.const_get('Foo') 

第二個電話則着眼於一流,望任何包含常數(卻沒有找到),然後看在它的祖先。 Object是一個祖先,並且具有頂級Foo模塊作爲常量,因此可以找到並返回該模塊。

這也解釋了添加額外::Foo s時的變化。頂層模塊上的const_get找到嵌套類,在嵌套類上查找繼承鏈並找到頂層模塊。

第四個例子,Foo.const_get('Foo::Bar'),這引起了一個例外,也可以解釋。它相當於

Foo.const_get('Foo').const_get('Bar') 

第一部分,Foo.const_get('Foo')是與上面相同的情況下,計算到Foo::Foo,所以整個事情現在實際上變成:

Foo::Foo.const_get('Bar') 

現在嵌套Foo類沒有按」 t包含一個Bar常量,並且查找繼承鏈,Object也沒有,所以結果是NameError