【问题标题】:Ruby: how does constant-lookup work in instance_eval/class_eval?Ruby:常量查找如何在 instance_eval/class_eval 中工作?
【发布时间】:2011-03-02 06:02:18
【问题描述】:

我正在使用 Pickaxe 1.9,但我对 instance/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,它存储您在顶层定义的所有常量),沿途检查混入

这是正确的吗? 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

【问题讨论】:

    标签: ruby constants instance-eval


    【解决方案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
    

    基本上,常量是准词法作用域的。这在 1.9.x 和 1.8.x 分支之间曾经是不同的,并且它使库的交叉兼容性很痛苦,所以他们把它改回来了。

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

    【讨论】:

    • 注意:从 Ruby 2.3 开始,这不再是真的了。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2017-04-22
      • 2011-05-23
      • 1970-01-01
      • 2011-03-26
      • 1970-01-01
      • 2012-05-05
      • 2013-05-13
      • 2011-04-01
      • 2022-11-13
      相关资源
      最近更新 更多