Ruby 中的全局过程并不是真正的全局过程。它们是方法,就像其他一切一样。特别是,当你定义一个看起来像全局过程的东西时,你实际上是在定义一个私有实例方法Object
。由于 Ruby 中的每一段代码都在对象的上下文中进行评估,因此您可以像使用全局过程一样使用这些方法,因为self
它是默认接收器,并且self
是一个其类继承自Object
.
所以这:
# file1.rb
def foo
puts 123
end
实际上相当于
# file1.rb
class Object
private
def foo
puts 123
end
end
现在您有一个名为 的“全局过程” foo
,您可以像这样调用它:
foo
之所以可以这样调用,是因为这个调用其实等价于
self.foo
并且self
是一个包含Object
在其祖先链中的对象,因此它继承了私有foo
方法。
[注意:确切地说,私有方法不能使用显式接收器调用,即使该显式接收器是self
. 所以,要真正学究起来,实际上相当于self.send(:foo)
而不是self.foo
。]
A.new.foo
你的问题是file2.rb
一个红鲱鱼:你也可以尝试Object.new.foo
or [].foo
or42.foo
并获得相同的结果。
顺便说一句:puts
并且require
它们本身就是这种“全局过程”的例子,它们实际上是私有方法Object
(或更准确地说,它们Kernel
是混入的私有方法Object
)。
附带说明:将调用放在类定义中确实是一种糟糕的风格,因为它使d 代码require
看起来像是在类中以某种方式限定了范围或命名空间,这当然是错误的。只需运行文件中的代码,仅此而已。require
require
所以,虽然
# file2.rb
class A
require 'file1.rb'
end
是完全有效的代码,它也很混乱。最好使用以下语义等价的代码:
# file2.rb
require 'file1.rb'
class A
end
这样一来,代码的读者就非常清楚了,代码file1.rb
内部没有范围或命名空间A
。
此外,通常最好不要使用文件扩展名,即使用require 'file1'
而不是require 'file1.rb'
. 这允许您将 Ruby 文件替换为例如本机代码(对于 MRI、YARV、Rubinius、MacRuby 或 JRuby)、.jar
或.class
文件中的 JVM 字节码(对于 JRuby)、文件中的 CIL 字节码.dll
(对于 IronRuby)和等等,而无需更改您的任何require
呼叫。
最后一条评论:规避访问保护的惯用方法是使用send
,而不是instance_eval
,即使用A.new.send(:foo)
而不是A.new.instance_eval {foo}
。