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.fooor [].fooor42.foo并获得相同的结果。
顺便说一句:puts并且require它们本身就是这种“全局过程”的例子,它们实际上是私有方法Object(或更准确地说,它们Kernel是混入的私有方法Object)。
附带说明:将调用放在类定义中确实是一种糟糕的风格,因为它使d 代码require看起来像是在类中以某种方式限定了范围或命名空间,这当然是错误的。只需运行文件中的代码,仅此而已。requirerequire
所以,虽然
# 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}。