David Flanagan的《Ruby编程语言》;Yukihiro Matsumoto,他们指出,变量前缀($,@,@@)是我们为能够省略方法调用周围的括号而付出的代价之一。有人能向我解释一下吗?
这是我不成熟的观点。如果我错了,请纠正我。
假设实例变量没有@
前缀,那么我们如何声明实例变量呢?
class MyClass
def initialize
# Here foo is an instance variable
self.foo = 'bar'
end
# Here foo is an instance method
def foo
end
def bar
# Call method. No doubt.
self.foo()
# Call method? Access instance variable?
self.foo
end
end
在上述情况下,使用假设语法初始化实例变量foo
self.foo = 'bar'
因为我们必须有一种方法告诉ruby解释器foo
属于当前实例,而不是局部变量。
那么在方法bar
中,如果方法调用中没有强制使用括号,解释器如何判断self.foo
是方法调用还是实例变量访问?
当括号缺失时,也许我们可以让实例变量遮蔽实例方法,但这会导致语言本身的语法不一致。例如,来自MyClass
定义之外:
obj = MyClass.new
# Call instance method
obj.foo
这是一个实例方法调用,因为所有实例变量从外部都是不可见的(至少在不使用元编程的情况下)。但是方法bar
中的self.foo
(没有括号)是实例变量访问。这种语法不一致可能会导致错误。此外,它也给解释器本身的实现带来了困难。
那么让实例方法遮蔽实例变量呢?然后,程序员必须确保实例变量名称不会与实例方法名称冲突,否则实例变量将无法访问。如果一个类有一个长的祖先链,这是非常困难的,因为不仅必须检查当前类定义中的冲突,还必须检查其祖先中的碰撞,这会破坏封装。
无论哪种方式,价格都远高于为实例变量添加前缀。对于类和全局变量,情况是相似的。
让我们看看一个例子:
def foo
10
end
p foo
#=> 10
foo = 'hi'
p foo
#=> hi
p foo()
#=> 10
如果我们在同一范围内定义一个名称与方法名称相同的局部变量,那么,由于变量名称优先,我们将失去与方法名称的绑定。为了消除歧义,将强制使用括号。
这本书似乎详细阐述了这一现象,以及为提供灵活性而做出的设计决策,其中需要使用像@
这样的符号来定义实例变量,以便可以在没有括号的情况下调用相应的访问器方法(var
&var=
),并且即使在现实中调用方法,也会给人一种访问变量的印象。
您会注意到Ruby变量开头的标点符号名称:全局变量以$为前缀,实例变量为前缀为@,类变量前缀为@@。这些前缀可能需要一点时间来适应,但过一段时间后你可能会请注意前缀告诉您变量为了消除Ruby的歧义,前缀是必需的非常灵活的语法。考虑可变前缀的一种方法是这是我们为能够省略括号而付出的代价方法调用。