为什么全局变量存在于 Ruby 中?

Why do Global Variables exist in Ruby?

我读过 C2Wiki 关于全局变量的内容,我有三个关于它们的问题(在 post 的底部)。主要问题是:如果全局变量这么糟糕,为什么 Ruby 有它们?

此外,我注意到 Ruby 中有关全局变量的一些有趣行为,这导致它们与常规全局级常量的工作方式不同。

1. 引用未定义的全局变量 returns nil。引用未定义的全局常量 returns a NameError:

2.2.3 :001 > $THING
 => nil 
2.2.3 :002 > THING
NameError: uninitialized constant THING
    from (irb):2
    from /Users/makerslaptop82/.rvm/rubies/ruby-2.2.3/bin/irb:15:in `<main>'

2. irb 初始化时同时定义了 $stdoutSTDOUT。您可以重新定义 $stdout,这不会影响 STDOUT:

2.2.3 :001 > $stdout
 => #<IO:<STDOUT>> 
2.2.3 :002 > STDOUT
 => #<IO:<STDOUT>> 
2.2.3 :003 > $stdout = IO.new(6)
 => #<IO:fd 6> 
2.2.3 :004 > $stdout
 => #<IO:fd 6> 
2.2.3 :005 > STDOUT
 => #<IO:<STDOUT>> 

我的问题是:

全局变量还不错。他们不是邪恶的。他们只是令人难以置信,令人难以置信的强大。这就是为什么你不应该使用它们。

全局变量是全局的——它们可以在代码的任何地方访问和修改。单个全局变量有可能影响您的所有 classes、所有函数、您加载到项目中的每个库或依赖项的所有 classes 和函数,每个单独项目的所有classes和函数加载你的项目作为依赖项,以及加载[=的项目48=]那些 项目等等,永远永远,在剩下的时间里。

第二个人开始对使用全局变量感到舒服,命名空间变得异常混乱,我们左右冲突,编程语言本身的稳定性受到威胁。这就是为什么反复强调不鼓励使用全局变量的原因。

不过全局变量也不错。它们就像标有 "for emergency vehicles only," 的高速公路车道或标有 "break glass in case of emergency."

的玻璃后面的火斧

完全有可能在遥远的将来的某个时候,您会遇到非常不寻常的情况,值得使用单个全局变量。但那一天不是今天。而且可能不是明天、一个月后或一年后。日常生活,日常代码——只是不需要全局变量的肆无忌惮的力量。


$stdout 是为什么全局变量有时很重要的一个很好的例子。 $stdout 是 ruby 中的默认流 - 如果未指定其他流,将打印内容的流。 $stdout 应该可以从每个 class 和每个库中的每个函数访问,因为它就像一个巨大的漏斗,将所有输出铲到一个位置。全世界都知道并同意 $stdout 存在于 ruby 中,其用途有据可查,因此其权力得到妥善管理。

这不要与 STDOUT 混淆,后者是一个常量,表示在 ruby 与其父程序(通常是终端)之间建立流的实际管道。默认情况下 $stdout = STDOUT,但 $stdout 可以更改为任何内容。如果你想让你的程序打印到一个文件,你可以把$stdout改成一个文件流。

我认为这个名字的选择不会让经验丰富的 rubyist 感到困惑。变量被设计为可以修改,常量被设计为常量。 $stdout 和 STDOUT 的区别在于,前者可以通过修改来改变你程序的标准输出位置,而后者是一个常量,始终指向 stdout 流。大写使世界变得不同,并传达了截然不同的含义。


至于为什么全局常量是未初始化的,而全局变量是nil,其实和globals没有关系。 Ruby 自动将所有变量初始化为 nil。您可以使用 @foo@@foo 等实例变量轻松地看到这一点。几乎在所有情况下,未定义的局部变量都会抛出 NameError,因为 ruby 无法判断它是变量还是方法。但在奇怪的情况下,它们也被初始化为 nil:

puts foo # => NameError: undefined local variable or method 'foo'
foo = 42 if false
puts foo # => nil

puts bar # => NameError
bar = bar
puts bar # => nil

在 Ruby 而非 中,自动初始化常量是一种有意识的设计选择。因为根据定义,常量是初始化一次然后永远不会更改的东西,它会破坏常量的定义,首先是 nil 然后在代码中稍后是不同的值。


我还应该提一下,全局常量被认为是可以接受的,即使在吹捧全局变量不好的人中也是如此。不同之处在于,常量只能分配一次,如果再次分配,通常会抛出警告或错误。这可以保护程序员免受冲突的全局常量可能导致问题的情况。