未执行的代码覆盖局部变量

Unexecuted code overrides local variable

给出的代码:

class Foo
  attr_reader :bar

  def initialize
    @bar = "abc"
    if false
      bar = "123"
    end
    p bar
  end
end

Foo.new

结果是

nil

为什么 p barinitialize 中打印 nil 而不是 abc

只要行

bar = "123"

被解析,即使不执行,它也会在作用域内将局部变量bar初始化为nil。这种行为(局部变量的特征)是由于局部变量采用了词法范围;它们的范围必须在不执行代码的情况下确定。当标记在局部变量和方法之间不明确时,它被解释为局部变量。

试试这个:

class Foo
  attr_reader :bar
  def initialize
    p "instance methods defined in Foo: #{self.methods(false)}"  
    @bar = "abc"
    p "defined? @bar: #{defined? @bar}"
    p "bar: #{bar}"
    p "defined? bar: #{defined? bar}"
    if false
      bar = "123"
    end
    p "defined? bar, 2nd time: #{defined? bar}"
    p "bar.nil? = #{bar.nil?}"
    p "self.bar = #{self.bar}"
    p "instance methods defined in Foo: #{self.class.instance_methods(false)}"  
  end
end

Foo.new
"instance methods defined in Foo: [:bar]"
"defined? @bar: instance-variable"
"bar: abc"
"defined? bar: method"
"defined? bar, 2nd time: local-variable"
"bar.nil? = true"
"self.bar = abc"
"instance methods defined in Foo: [:bar]"

行数:

"defined? @bar: instance-variable"
"defined? bar: method"

说明@bar是实例变量,bar是实例方法,即attr_reader :bar创建的@bar的getter方法。之前

if false
  bar = "123"
end

被求值,Ruby进入if子句。她在那里看到 bar = "123"。如果调用,这会将值 "123" 分配给未初始化的局部变量 bar.

bar= 不能是实例方法(例如,@bar 的 setter),因为名称以等号结尾的任何方法都必须在 [=81= 上调用]显式接收者。 (它的工作方式允许编码人员使用与实例变量同名的局部变量,减去前导 @。)

什么是 "explicit" 接收器?如果 Foo 有一个 public 实例方法 buz,你可以写:

foo = Foo.new
foo.buz

foo 是方法 buz 的显式接收者。要从 Foo 的实例方法之一中调用 buz,您可以使用显式接收器:

self.buz

或者直接写:

buz

在这种情况下 self 隐式 接收者。

因为 bar= 只能用显式接收者来写,我们会写:

attr_writer :bar
...
self.bar = "123"

调用@bar的setter。

我们在哪里?啊,我们刚刚得出结论:

if false
  bar = "123"
end
如果执行了if子句,

会为局部变量bar赋值,不管是否存在方法Foo#bar=.

因为false是,好吧,falseif子句的内容没有被执行,所以bar的值没有从nil.

重要的是局部变量 bar 和实例变量 @bar 就像 night@day 一样彼此不同。我们可以很容易地证明如下:

a  = 'cat'
@a = 'dog'
a  #=> "cat" 
a  = 'pig'
@a #=> "dog" 

p bar 不会 return "abc" 因为 bar@bar 是不同的。 @bar是一个实例变量,但是当你不带前缀@调用它时,ruby搜索局部变量。

一般在ruby,实例变量和全局变量未定义时,returnnil。但是当 undefined 时局部变量会抛出错误。但是在您的情况下,您已尝试初始化变量,即使由于 if false 而从未初始化它,它仍然已被解析,这足以让 ruby 解释器 return nil.

这在this link

中已经解释得很清楚了