给定代码:
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
?
答案 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=
。
因为false
是false
,所以if
子句的内容不会被执行,因此bar
的值不会从{{1}更改}。
重要的是,局部变量nil
和实例变量bar
与@bar
和night
完全不同。我们可以很容易地表明如下:
@day
答案 1 :(得分:1)
只要行
bar = "123"
解析,它将范围内的局部变量bar
初始化为nil
,即使它没有被执行。这种行为(局部变量的特征)是由于局部变量占据了词法范围;必须在不执行代码的情况下确定其范围。当一个令牌在局部变量和方法之间不明确时,它被解释为局部变量。
答案 2 :(得分:1)
p bar
不会返回"abc"
,因为bar
和@bar
不同。 @bar
是一个实例变量,但是当你在没有前缀@的情况下调用它时,ruby会搜索一个局部变量。
通常在ruby,实例变量和未定义的全局变量中,返回nil
。但是当未定义时,局部变量会抛出错误。但是在你的情况下,你已经尝试初始化变量,即使它由于if false
从未被初始化,它仍然被解析,这足以让ruby解释器返回nil
。