考虑以下代码:
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
实例上正确分配常量?
答案 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_eval和this discussion,了解使用class_eval
(创建常量的补充)进行常量查找的方法。 (搜索"class_eval"
)。