Ruby中的全局过程并不是真正的全局过程,它们像其他所有方法一样都是方法。特别地,当您定义类似全局过程的内容时,实际上正在定义
Object
的私有实例方法。由于Ruby中的每个代码片段都在对象的上下文中进行评估,因此可以使用这些方法就像它们是全局过程一样,因为默认接收者是
self
,而
self
是其类继承自
Object
的对象。因此,这段代码:
def foo
puts 123
end
实际上等价于
class Object
private
def foo
puts 123
end
end
现在你有一个名为
foo
的“全局过程”,你可以像这样调用它:
foo
你可以这样称呼它的
原因是,该调用实际上等同于
self.foo
而 self
是一个包含在其祖先链中的 Object
对象,因此它继承了私有的 foo
方法。
[注意:准确地说,私有方法不能用显式接收者调用,即使这个显式接收者是 self
。所以,更正确的是等价于 self.send(:foo)
而不是 self.foo
。]
你在 file2.rb
中的 A.new.foo
是一个红鲱鱼:你同样可以尝试 Object.new.foo
、[].foo
或者 42.foo
并得到相同的结果。
顺便提一下:puts
和 require
本身就是这种「全局过程」的例子,它们实际上是 Object
(或者更确切地说,是混入到 Object
中的 Kernel
)上的私有方法。
另外注意一点:在类定义中放置 require
调用非常不好的风格,因为这会让人误认为 require
的代码在某种程度上被作用域化或命名空间化了,而这当然是错误的。 require
只是运行文件中的代码,仅此而已。
因此,虽然
class A
require 'file1.rb'
end
虽然“<br>
”是有效的代码,但也很容易令人困惑。使用以下语义等效的代码会更好:
require 'file1.rb'
class A
end
这样代码的读者就很清楚,file1.rb
在任何情况下都不会被作用域或命名空间在A
内。
此外,通常更喜欢省略文件扩展名,即使用require 'file1'
而不是require 'file1.rb'
。这样可以将Ruby文件替换为例如本地代码(对于MRI、YARV、Rubinius、MacRuby或JRuby)、JVM字节码的.jar
或.class
文件(对于JRuby)、CIL字节码的.dll
文件(对于IronRuby)等,无需更改任何require
调用。
最后一个评论:规避访问保护的惯用方式是使用send
,而不是instance_eval
,即使用A.new.send(:foo)
代替A.new.instance_eval {foo}
。
A.new.instance_eval{foo}
吗?对我来说不起作用(ruby 1.9.2)。 - knutrequire
和include
? - Andrew Grimm