我试图理解Ruby对象和self关键字。
假设我有一个类:
class CashRegister
attr_accessor :total
def initialize(total)
@total = total
end
def add(amount)
self.total = self.total + amount
end
end
cr = CashRegister.new(5)
puts cr.total #=> 5
cr.add(10)
puts cr.total #=> 15
假设我在上面的添加方法中删除了 self 关键字:
def add(amount)
total = total + amount
end
我收到一个错误:
cr = CashRegister.new(5)
puts cr.total #=> 5
cr.add(10) #=> this throws an error: ./lib/test.rb:28:in `add': undefined method `+' for nil:NilClass (NoMethodError)
我假设这是因为我需要 self 关键字来引用实例变量,总计
假设我还有另一个类似的类:
class School
attr_accessor :name, :roster
def initialize(name)
@name = name
@roster = {}
end
def add_student(student, grade)
roster[grade] = roster[grade] || []
roster[grade] << student
end
end
school = School.new("Jefferson High")
puts school.roster #=> {}
school.add_student("Sam", 10)
puts school.roster #=> {10=>["Sam"]}
为什么我不需要自己add_student?
tl;DR:foo = value
将始终引用局部变量foo
而不是方法调用self.foo=(value)
。
我假设这是因为我需要 self 关键字来引用实例变量,总计
否,total
是局部变量,而不是实例变量。@total
是一个实例变量。局部变量存在于当前范围内,就像单个方法调用一样。实例变量与对象一起保留。
您需要self
关键字来引用方法total=
。让我们潜入。
attr_accessor :total
声明了两种方法,total
和total=
。这些是用于获取和设置实例变量@total
的包装器。以下代码执行等效操作。
def total
@total
end
def total=(value)
@total = value
end
请注意,该方法名为total=
,这将在一会儿变得很重要。
考虑到这一点,让我们看一下您的代码。
def add(amount)
self.total = self.total + amount
end
(几乎(Ruby 中的所有内容实际上都是方法调用。以上是在self
上调用total=
方法的语法糖。
def add(amount)
self.total=(self.total + amount)
end
现在,如果我们像这样删除self
会发生什么?
def add(amount)
total = total + amount
end
在 Ruby 中,self
是可选的。Ruby 将确定total
是指方法total
还是局部变量total
。局部变量优先,赋值始终分配给局部变量。
total = total + amount
的工作方式如下:
def add(amount)
total = self.total + amount
end
赋值始终是局部变量。
为了进一步说明,如果我们先宣布total
呢?
def add(amount)
total = 23
self.total = total + amount
end
现有的局部变量total
优先于total()
方法。total + amount
是指局部变量total
,因此cr.add(10); puts cr.total
将为 33。
total = ...
将始终引用局部变量total
。因此,如果要使用方法赋值,则必须显式使用self.total=
。在其他情况下,您可以删除self
.并避免与方法同名的局部变量。
def add(amount)
# self.total = self.total + amount
self.total = total + amount
end
为什么我不需要自己add_student?
因为没有歧义。您没有分配给局部变量roster
。
def add_student(student, grade)
roster[grade] = roster[grade] || []
roster[grade] << student
end
roster[grade]
真的很self.roster.[]=(grade)
.您正在调用返回Hash
的self.roster
,然后在该哈希上调用[]=
方法以为其提供新的键/值对。
同样,roster[grade] << student
self.roster.[](grade).<<(student). Get a
哈希, call [
[]](https://ruby-doc.org/core/Hash.html#method-i-5B-5D) on it to retrieve the
数组and call [
<<](https://ruby-doc.org/core/Array.html#method-i-3C-3C) on that
数组'。
def add_student(student, grade)
self.roster.[]=(grade) = self.roster.[](grade) || []
self.roster.[](grade).<<(student)
end
是的,[]
、[]=
和<<
都是方法名称,就像其他任何方法一样。
一旦你理解了语法 sugar 下的方法调用,很多 Ruby 的谜团就会消失,并且像[]
、[]=
、<<
等都是方法名。