未执行的代码会覆盖本地变量

时间:2015-08-23 00:05:46

标签: ruby local-variables ambiguity

给定代码:

class Foo
  attr_reader :bar

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

Foo.new

结果是

nil

为什么p bar initialize打印nil而不是abc

3 个答案:

答案 0 :(得分:2)

试试这个:

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是实例方法,即@bar创建的attr_reader :bar的getter方法。前

if false
  bar = "123"
end

被评估,Ruby对等if子句。在那里,她看到bar = "123"。如果调用,则会将值"123"分配给未初始化的局部变量bar

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

什么是"显式"接收器?如果Foo有公共实例方法buz,您可以写:

foo = Foo.new
foo.buz

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

self.buz

或者只写:

buz

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

由于bar=只能使用显式接收器编写,我们会写:

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

调用@bar的二传手。

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

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

将为局部变量if分配值,无论是否存在方法Foo#bar=

因为falsefalse,所以if子句的内容不会被执行,因此bar的值不会从{{1}更改}。

重要的是,局部变量nil和实例变量bar@barnight完全不同。我们可以很容易地表明如下:

@day

答案 1 :(得分:1)

只要行

bar = "123"
解析

,它将范围内的局部变量bar初始化为nil,即使它没有被执行。这种行为(局部变量的特征)是由于局部变量占据了词法范围;必须在不执行代码的情况下确定其范围。当一个令牌在局部变量和方法之间不明确时,它被解释为局部变量。

答案 2 :(得分:1)

p bar不会返回"abc",因为bar@bar不同。 @bar是一个实例变量,但是当你在没有前缀@的情况下调用它时,ruby会搜索一个局部变量。

通常在ruby,实例变量和未定义的全局变量中,返回nil。但是当未定义时,局部变量会抛出错误。但是在你的情况下,你已经尝试初始化变量,即使它由于if false从未被初始化,它仍然被解析,这足以让ruby解释器返回nil

this link

清楚地解释了这一点