我已经阅读了关于全局变量的C2Wiki,我有三个关于它们的问题(在这篇文章的底部)。主要问题是:如果全局变量如此糟糕,为什么 Ruby 会有它们?
另外,我注意到 Ruby 中关于全局变量的一些有趣行为,这导致它们的工作方式与常规的全局级常量不同。
1.引用未定义的全局变量返回nil
。引用未定义的全局常量返回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
使用定义$stdout
和STDOUT
进行初始化。您可以重新定义$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>>
我的问题是:
- 如果全局变量如此糟糕,为什么 Ruby 会有它们?
- 为什么引用未定义的全局变量返回
nil
而不是NameError
?这个选择是故意的吗?为什么?
在一个 - 程序中拥有两个名称几乎相同的STDOUT版本是否有危险?(我假设还有其他全局定义的对象也适用于)
全局变量还不错。他们不是邪恶的。它们简直令人难以置信,难以置信地强大。这就是为什么你不应该使用它们。
全局变量是全局的 - 可以在代码中的任何位置访问和修改它们。单个全局变量可能会影响所有类、所有函数、加载到项目中的每个库或依赖项的所有类和函数,以及将项目作为依赖项加载的每个项目的所有类和函数,以及加载这些项目的项目, 如此等等,永远永远,在剩下的时间里。
第二个人开始对使用全局变量感到自在,命名空间变得非常混乱,我们左右发生冲突,编程语言本身的稳定性受到威胁。这就是为什么坚决不鼓励使用全局变量的原因。
但全局变量也不错。它们就像标有"仅供紧急车辆使用的"的高速公路车道,或者像那些标有"紧急情况下打破玻璃"的玻璃后面的消防斧。
完全有可能在遥远的将来的某个时候,你会有一个令人难以置信的不寻常的情况,值得使用单个全局变量。但那一天不是今天。而且可能不是明天,也不是一个月后,也不是一年后。日常生活,日常代码 - 它只是不需要全局变量的肆无忌惮的力量。
$stdout
是一个很好的例子,说明为什么全局变量有时很重要。$stdout
是 Ruby 中的默认流 - 如果未指定其他流,则将在其中打印内容。$stdout
应该可以从每个库中的每个类和每个函数访问,因为它就像一个巨大的漏斗,将所有输出铲到一个位置。全世界都知道并同意红宝石中存在$stdout
,它的用途有据可查,因此它的力量得到了很好的管理。
不要将其与STDOUT
混淆,后者是一个表示实际管道的常量,它在 ruby 及其父程序(通常是终端)之间设置流。 默认情况下$stdout = STDOUT
,但$stdout
可以更改为任何内容。如果希望程序打印到文件,可以将$stdout
更改为文件流。
我不认为这个名字的选择对于一个经验丰富的红宝石学家来说是令人困惑的。变量被设计为可修改,常量被设计为常量。$stdout 和 STDOUT 之间的区别在于,可以修改前者以更改程序的标准输出位置,而后者是一个常量,始终指向 stdout 流。大写字母使世界有所不同,并传达了非常不同的含义。
至于为什么全局常量是未初始化的,而全局变量是nil
的,那其实和全局变量无关。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
常量,然后在代码中稍后使用不同的值。
我还应该提到,全局常数被认为是可以接受的,即使在那些吹捧全局变量不好的人中也是如此。不同之处在于常量只能赋值一次,如果再次赋值,通常会引发警告或错误。这可以保护程序员免受冲突的全局常量可能导致问题的情况。