在引用 ruby 中的类实例变量时了解 self 关键字?



我试图理解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声明了两种方法,totaltotal=。这些是用于获取和设置实例变量@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).您正在调用返回Hashself.roster,然后在该哈希上调用[]=方法以为其提供新的键/值对。

同样,roster[grade] << studentself.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 的谜团就会消失,并且像[][]=<<等都是方法名。

最新更新