我们定义一个函数foo:
def foo(s)
case s
when'foo'
x = 3
puts x.inspect
when 'bar'
y = 4
puts y.inspect
end
puts x.inspect
puts y.inspect
end
然后我们这样调用它:
1.9.3p194 :017 > foo('foo')
in foo scope
3
in outer scope
3
nil
=> nil
1.9.3p194 :018 > foo('bar')
in bar scope
3
in outer scope
nil
3
=> nil
为什么在这两种情况下函数都不会抛出关于未注册的局部变量的错误?在第一种情况下,变量 y
似乎不应该存在,因此您不能在外部范围内对其调用 inspect
;在第二种情况下,x
也是如此。
这是另一个类似的例子:
def test1
x = 5 if false
puts x.inspect
end
def test2
puts x.inspect
end
然后:
1.9.3p194 :028 > test1
nil
=> nil
1.9.3p194 :029 > test2
NameError: undefined local variable or method `x' for main:Object
这是怎么回事?看起来 Ruby 正在将变量声明提升到外部范围,但我不知道这是 Ruby 所做的事情。 (搜索“ruby 提升”只会出现有关 JavaScript 提升的结果。)
When the Ruby parser sees the sequence identifier, equal-sign, value,
as in this expression
x = 1
it allocates space for a local variable called x
. The creation of the
variable—not the assignment of a value to it, but the internal
creation of a variable—always takes place as a result of this kind of
expression, even if the code isn’t executed! Consider this example:
if false
x = 1
end
p x # Output: nil
p y # Fatal Error: y is unknown
The assignment to x
isn’t executed, because it’s wrapped in a failing
conditional test. But the Ruby parser sees the sequence x = 1
, from
which it deduces that the program involves a local variable x
. The
parser doesn’t care whether x
is ever assigned a value. Its job is
just to scour the code for local variables for which space needs to
be allocated. The result is that x
inhabits a strange kind of variable limbo.
It has been brought into being and initialized to nil
.
In that respect, it differs from a variable that has no existence at
all; as you can see in the example, examining x
gives you the value
nil
, whereas trying to inspect the non-existent variable y
results
in a fatal error. But although x
exists, it has not played any role in
the program. It exists only as an artifact of the parsing process.
扎实的 Rubyist 第 6.1.2 章