Ruby:在instance_eval / class_eval中常量查找是如何工作的?

时间:2010-06-10 15:38:51

标签: ruby constants instance-eval

我正在通过Pickaxe 1.9工作,我对实例/ class_eval块中的常量查找感到困惑。我正在使用1.9.2。

Ruby似乎在处理* _eval块中的常量查找方式与方法查找相同:

  1. 在receiver.singleton_class(加上mixins)中寻找定义;
  2. 然后在receiver.singleton_class.superclass(加上mixins);
  3. 然后继续使用本征链,直到你到达#<Class:BasicObject>;
  4. 其超类是Class;
  5. 然后上升祖先链的其余部分(包括Object,它存储您在顶层定义的所有常量),检查沿途的mixins
  6. 这是对的吗? Pickaxe的讨论有点简洁。

    一些例子:

    class Foo
      CONST = 'Foo::CONST'
      class << self
        CONST = 'EigenFoo::CONST'
      end
    end
    
    Foo.instance_eval { CONST } # => 'EigenFoo::CONST'
    Foo.class_eval { CONST } # => 'EigenFoo::CONST', not 'Foo::CONST'!
    Foo.new.instance_eval { CONST } # => 'Foo::CONST'
    

    在class_eval示例中,Foo-the-class不是沿着Foo-the-object的祖先链停止的!

    以mixins为例:

    module M
      CONST = "M::CONST"
    end
    module N
      CONST = "N::CONST"
    end
    
    class A
      include M
      extend N
    end
    
    A.instance_eval { CONST } # => "N::CONST", because N is mixed into A's eigenclass
    A.class_eval { CONST } # => "N::CONST", ditto
    A.new.instance_eval { CONST } # => "M::CONST", because A.new.class, A, mixes in M
    

2 个答案:

答案 0 :(得分:1)

在1.9.2中,常量查找已再次更改,等同于1.8.7行为。

class A
  class B
    class C
    end
  end
end

A.class_eval { B } # => NameError
A.instance_eval { B } # => NameError
A.new.instance_eval { B } # => A::B

基本上,常量是准词法范围的。这个USED在1.9.x和1.8.x分支之间是不同的,它使库交叉兼容性变得很痛苦,所以他们改回来了。

Yehuda Katz's (successful) appeal to restore 1.8 behavior

答案 1 :(得分:0)

常量实际上是词法范围的,因此您无法在定义它们的模块层次结构之外进行简短访问。有一个很好的解释here和稍微偏离主题,但很好的阅读here