为什么在Class.new代码块中直接分配常量不起作用

时间:2018-09-27 04:00:08

标签: ruby constants

考虑以下代码:

c1 = Class.new { ANSWER = 42 }
#⇒ #<Class:0x00556c8fc09c60> < Object
c1.constants
#⇒ []
c1.new.class.constants
#⇒ []
c1.new.singleton_class.constants
#⇒ []
Object.constants.grep /ANS/
#⇒ [:ANSWER]

常量似乎是在Object上定义的。但是,通过显式调用const_set,常量分配可以完美地工作:

c2 = Class.new { const_set :ANSWER, 42 }
#⇒ #<Class:0x00556c8f7f3568> < Object
c2.constants
#⇒ [:ANSWER]
c2.new.class.constants
#⇒ [:ANSWER]
c2.new.singleton_class.constants
#⇒ [:ANSWER]

我的问题是:是什么阻止在第一个代码段的Class.new实例上正确分配常量?

2 个答案:

答案 0 :(得分:5)

There are three implicit contexts in Ruby

  • self(无接收者消息发送的上下文和实例变量)
  • 默认定义def方法定义表达式的上下文,没有明确的目标,即def bar而不是def foo.bar
  • 默认常量定义点

不幸的是,虽然我上面链接的文章列出了所有这三个文章,但它仅讨论了前两个,并将默认的常量定义点推迟到以后的文章,而从未写过。

无论如何,重要的是要牢记这三个上下文,并注意它们何时更改以及何时不更改。

尤其是,仅 块会更改词法上下文,而不会改变。块不会更改self,它不会更改默认定义,也不会更改默认常量定义点

但是,有些方法的显式目的是更改这三个上下文中的一个或多个。

*_eval系列方法更改了self(上下文1)和默认的definee(上下文2),但是它没有更改默认常量定义点(上下文3)。特别是,所有*_eval*_exec)方法都将self设置为接收方。 instance_*版本将默认定义定义为接收者的单例类,module_*class_*版本将默认定义定义为接收者。

但是,默认常量定义点未更改,因此常量定义(和查找)的工作原理与以前一样:定义转到最近的词法包围模块定义,查找从最近的词法包围开始模块定义,并按继承顺序按词法向外和动态向上进行。

据我所知,更改默认常量定义点的 only构造module / class定义。

答案 1 :(得分:1)

Class::new的文档指出:“如果给出了一个块,则将其传递给类对象,并且在此类的上下文中对块进行评估,例如class_eval。”,这意味着/ p>

c = Class.new { ANSWER = 42 }

等同于

c = Class.new
c.class_eval  { ANSWER = 42 }

class_eval在顶层创建常量,因为它是在顶层的c上调用的。 c.class_eval { ANSWER = 42 }实际上与

相同
ANSWER = 42

Object上创建一个常量。

Object::ANSWER
  #=> 43

这是另一种方式。

NUMBER = 43
Object::NUMBER
  #=> 43

c = Class.new
c.const_set(:NUMBER, 48)
c::NUMBER
  #=> 48
c.class_eval { puts NUMBER }
43

这是另一个例子。

class F
  class G; end
  G.class_eval { HIPPO = 5 }
end

F::HIPPO
  #=> 5
F::G::HIPPO
  #=> #NameError (uninitialized constant F::G::HIPPO)
  #   Did you mean?  F::HIPPO

但是,

class F
  class G
    class_eval { HIPPO = 5 }
  end
end

F::HIPPO
  #=> NameError (uninitialized constant F::HIPPO)
F::G::HIPPO
  #=> 5

读者:请参见Module#class_evalthis discussion,了解使用class_eval(创建常量的补充)进行常量查找的方法。 (搜索"class_eval")。