为什么我可以引用从未运行的if / unless / case语句之外的变量?

为什么以下代码不会抛出错误?

if false x = 0 end x #=> nil 

以下确实会引发错误:

 y # NameError: undefined local variable or method `x' for main:Object 

unlesscase语句也会发生同样的事情。

这是因为Ruby解析器的工作原理。 变量由解析器定义,解析器逐行遍历代码,无论它是否实际执行。

一旦解析器看到x = ,它就定义了当前范围内的局部变量x (值为nil )。 由于if / unless / case / for / while不创建新范围,因此x在代码块之外定义并可用。 并且由于内部块永远不会被评估为条件为假,因此x未被赋值(因此nil )。

这是一个类似的例子:

 defined?(x) and x = 0 x #=> nil 

请注意,这是对所发生情况的相当高级别的概述,并不一定完全是解析器的工作方式。

这与Ruby的范围规则的怪癖有关。

在ruby中,一个未修饰的变量x本身可能是局部变量或方法调用 – 语法无法分辨哪个。 由解析器解析局部变量引用,由解析器决定。 规则很简单:如果已在本地范围内看到对同名变量的赋值,则引用是局部变量,并且引用绑定到该局部变量。 否则,它是一个方法调用,它将在运行时被查找。

Ruby中的局部变量引用被优化为数组查找(每个局部变量被赋予一个’slot’,并且由解析器生成的绑定局部变量引用被转换为槽引用)。 使用所有nil初始化数组:

 /* initialize local variables */ for (i=0; i < local_size; i++) { *sp++ = Qnil; } 

因此,如果您引用尚未分配的局部变量,则通过绑定的本地引用(只有在同一本地范围内的引用之上有跳过的赋值时才会发生),您将得到nil

我认为你的问题很有趣,所以我试着查一下,发现了这个: 我不明白ruby的局部范围

正确的答案似乎是Jorg。

让我们看一下当您尝试访问未初始化的变量时会发生什么:

 NameError: undefined local variable or method `UNDECLAREDVAR' for main:Object 

该exception表明它无法评估变量或方法。 它不抛出相同exception的原因是因为未初始化的局部变量设置为nil。 所以puts x是可以的,因为解释器知道x是可变的但未初始化而不是方法。