再探Ruby中的load。
Ruby中的load method用来加载文件,也就是将另一个ruby文件中的代码加载进来了。
《Ruby元编程》中提到,使用load时,可能会出现常量污染当前程序的命名空间的问题,添加了true这个参数后,Ruby会创建一个匿名模块,用它作为命名空间来容纳加载文件中定义的所有常量,从而避免污染的问题,但是书中并没有给出例子来详细说明。
来看两个例子感受下:
同一个目录下,存在两个文件,a.rb, b.rb,a中加载了b.rb文件。
# b.rb
## 只定义了一个普通的常量Y
Y = "hello"
a.rb中加载b.rb
# a.rb
load './b.rb'
Y = "hi"
p Y
#=> warning: already initialized constant Y
此时,执行a.rb,会报错,显示:warning: already initialized constant Y
也就是加载进来的常量Y与a.rb中的常量Y重了,系统报错显示已经初始化了Y。
如果添加了true,则输出”hi”。
# a.rb
load './b.rb', true
Y = "hi"
p Y
#=> hi
这里Y是一个常规的常量,ruby中类名,模块名也是常量,如果加载同名的类/模块会出现什么情况?
修改下b.rb文件:
# b.rb
module Mymodule
def hi
p "method hi in b.rb"
end
end
修改下a.rb文件:
# a.rb
load './b.rb'
class Mymodule
def hello
p "hello"
end
end
Mymodule.new.hello
#=> Mymodule is not a class
可以看到,加载后,执行a.rb时,报错,显示Mymodule is not a class,因为此时加载进来了一个同名的Mymodule,它是一个module,而a.rb文件中,定义了一个同名的Mymodule类,这个类其实并没有创建成功,这里稍微提一下,执行a.rb时,报错不是发生在执行Mymodule.new.hello时,而是在定义Mymodule这个类的地方报错。
如果添加上true,就正常了。
# a.rb
load './b.rb', true
class Mymodule
def hello
p "hello"
end
end
Mymodule.new.hello
#=> hello
这里,你可能会疑惑,添加了true之后,原本来自b.rb中那个Y,module Mymodule去了哪里?书中提到,Ruby会创建一个匿名模块,用它作为命名空间来容纳加载文件中定义的所有常量, 加载完成后,该模块会被销毁,也就是等同于他们根本就没有加载进来了?怎么知道它们真的没有加载进来?
可以通过Module的类方法constants来判断,该方法返回当前顶层程序中的所有常量。
修改下b.rb ,改回原来的样子:
# b.rb
Y = "hello"
修改下 a.rb:
# a.rb
# 未加载b.rb
p Module.constants
#=> [:Integer, :Float, :String, :Array, :Hash, :NilClass, :STDERR, :STDIN, :NIL, :Delegator, :STDOUT, :ARGF, :SimpleDelegator, :UncaughtThrowError, :FileTest, :File, :GC, :Fiber, :FiberError, :Rational, :ObjectSpace, :Gem, :DidYouMean, :Complex.......]
p Module.constants.count
#=> 117
p Module.constants.include? :Y
#=> false
加载b.rb,无true:
# a.rb
load './b.rb'
p Module.constants
#=> [:Integer, :Float, :String, :Array, :Hash, :NilClass, :STDERR, :STDIN, :NIL, :Delegator, :STDOUT, :ARGF, :SimpleDelegator, :UncaughtThrowError, :FileTest, :File, :GC, :Fiber, :FiberError, :Rational, :Y, :ObjectSpace, :Gem, :DidYouMean, :Complex.......]
p Module.constants.count
#=> 118
p Module.constants.include? :Y
#=> true
此时Y加载进来了,常量总数加1。
加载b.rb,有true:
# a.rb
load './b.rb', true
p Module.constants
#=> [:Integer, :Float, :String, :Array, :Hash, :NilClass, :STDERR, :STDIN, :NIL, :Delegator, :STDOUT, :ARGF, :SimpleDelegator, :UncaughtThrowError, :FileTest, :File, :GC, :Fiber, :FiberError, :Rational, :ObjectSpace, :Gem, :DidYouMean, :Complex.......]
p Module.constants.count
#=> 117
p Module.constants.include? :Y
#=> false
输出结果与未加载b.rb完全一致。
也就是书中说的那样,添加true后,Ruby会创建一个匿名模块,用它作为命名空间来容纳加载文件中定义的所有常量, 加载完成后,该模块会被销毁。
等同于load添加true后,所有的常量都没有加载进来。当然这里的常量,包括类名,模块名。