Ruby常量尋找路徑問題深入研究_ruby專題

來源:互聯網
上載者:User

Ruby 的常量尋找路徑問題是一直困擾我的一個問題,在工作中遇到過好幾次,一直沒有徹底弄清楚到底為什麼,最近在讀一本書《Ruby 元編程》,對 Ruby 物件模型有了更深入的認識,另外讀了一篇 blog《Everything you ever wanted to know about constant lookup in Ruby》, 讓我總算把 Ruby 常量尋找路徑這個問題搞得比較清楚。

第一個遇到的問題,我還曾經在 Ruby-China 上發過帖。

複製代碼 代碼如下:

module M1
  CT = "ok"
end

class C1
  CK = "ck"
  include M1

  def self.method1
    puts self
    puts "#{CK} in method1"
    puts "#{CT} in method1"
  end

  class << self
    def method2
      puts self
      puts "#{CK} in method1"
      puts "#{CT} in method2"
    end
  end
end

C1.method1
C1.method2

輸出結果是

複製代碼 代碼如下:

C1
ck in method1
ok in method1
C1
ck in method2
NameError: uninitialized constant Class::CT
    from (irb):16:in `method2'

這是我在重構薄荷網代碼時候遇到的問題,method1 和 method2 都是常見的類方法的定義方面,我向來認為它們是等價可替換的寫法,但是從實際執行的結果看,它們裡面的常量尋找路徑不一樣。

如果我把 M1 的定義改成下面的樣子:

複製代碼 代碼如下:

module M1
  def self.included(base)
    base.extend(self)
  end
  CT = "ok"
end

執行結果是:

複製代碼 代碼如下:

C1
ck in method1
ok in method1
C1
ck in method2
ok in method2

還有一個問題是也是經常遇到的,抽象成問題代碼如下:

複製代碼 代碼如下:

module A
  module M
    def a_method
      #...
    end
  end
end

class A::B
  include M
end

會報異常:

複製代碼 代碼如下:

NameError: uninitialized constant A::B::M
  from (irb):10:in `<class:B>'

Ruby 常量尋找時依據兩條路徑

A. Module.nesting
B. open class/module 的 ancestors

A 比 B 優先,A 找不到了才到 B 中尋找。

A.Module.nesting 的概念比較容易理解,它是指代碼位置的 module 嵌套情況,它是一個數組,從最內層的嵌套一直到最外層的嵌套,如果沒有嵌套,數組為空白。任何一處代碼位置都有 Module.nesting 值,可以通過下面的代碼列印出各個位置的 Module.nesting 值。

複製代碼 代碼如下:

p Module.nesting

module A
  module B
    p Module.nesting
    module C
      p Module.nesting
    end
  end
end

module A::B
  p Module.nesting
end

輸出是:

複製代碼 代碼如下:

[]
[A::B, A]
[A::B::C, A::B, A]
[A::B]

大家有沒有注意到,module A::B 這種快捷寫法會導致 A 不在 Module.nesting 裡,這就是上述第二個問題的根源,因為 M 是 A module 下的常量,module A::B 寫法導致不會尋找 A::M。

說完 A Module.nesting,再說一下 B open class/module 的 ancestors,這個問題相對複雜很多。簡單的說,在 Ruby 代碼的任何位置,都有一個 self 存在,同樣也有一個 open class/module 存在,在模組和類定義處,它通常就是對應的模組和類,在方法內部,它是方法對應的類。對於 ancestors,我們可以通過代碼位置 open class/module 的 ancestors 方法取得。

(備忘:ancestors 在引入 singleton_class 概念之後變得有點複雜,如不清楚可參考《Ruby 元編程》)

上述第一個問題: 在method1 中 A 是 [C1] open class/module 是 C1,所以 ancestors 是 [C1, M1, Object, Kernel, BasicObject] CK 在 A 可以找到,CT 在 B 可以找到。

method2 中 A 是 [C1] open class/module 是 C1 的 singleton_class , 所以 ancestors 是 [Class, Module, Object, Kernel, BasicObject] CK 在 A 可以找到,CT 在 A 和 B 都找不到。

對於

複製代碼 代碼如下:

module M1
  def self.included(base)
    base.extend(self)
  end
  CT = "ok"
end

可運行,是因為這時,在 method2 中,open class/module C1 的 singleton_class 擴充了 M1,所以 ancestors 變成了 [M1, Class, Module, Object, Kernel, BasicObject]。

至此,這兩個困擾我多時的問題終於徹底搞清楚了。這個過程給我的一個體會是:面對技術上的一些疑問,如果只是淺嘗輒止,是永遠不能夠真正掌握它的,只有深入專研,透徹理解它的原理,才能夠真正掌握它,獲得真正的能力提升。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.