了解在 Ruby 中初始化时分配的变量的访问

Understanding the access of a variable assigned in initialize in Ruby

作为初学者,我不太了解 self 所以我很难理解 self.blogs 是如何初始化的,blogs 然后是 self.blogsadd_blog 方法之后的下一行,都在下面的代码中一起工作。

为什么add_blog方法中的blogs和initalize中的self.blogs访问同一个变量? 那为什么后面用self.blogsblogs数组排序呢?

此外,如果我在初始化中使用 @blogs 而不是 self.blogs 会有影响吗?

class User
  attr_accessor :username, :blogs

  def initialize(username)
    self.username = username
    self.blogs    = []
  end

  def add_blog(date, text)
    added_blog = Blog.new(date, self, text)
    blogs << added_blog
    self.blogs = blogs.sort_by { |blog| blog.date }.reverse
    added_blog
  end
end

@variable 将直接访问 class 的实例变量。写入 self.variable 将向对象发送一条消息 variable。默认情况下,它将 return 实例变量,但它可以根据您设置对象的方式做其他事情。它可以是对方法的调用,或子class,或其他任何东西。

调用blogsself.blogs的区别完全取决于语法。如果你使用像 rubocop 这样的自以为是的语法检查器,它会告诉你你有多余的使用 self

对于大多数对自身的方法调用,self.method_name 等同于 method_name。但是,名称以 = 结尾的方法并非如此。

首先要注意的是,self.blogs = etc 不会调用名为 blogs 的方法,然后以某种方式调用 'assign etc to it';该行调用方法 blogs=,并将 etc 作为参数传递给它。

您不能像使用其他方法调用那样将其缩短为 blogs = etc 的原因是因为 blogs = etc 与创建名为 blogs 的新局部变量没有区别.

在上一行中,当您看到一个裸露的 blogs 时,即 也是一个方法调用 ,并且可以很容易地写成 self.blogs.用隐式接收器编写它只是更短。当然,blogs 作为局部变量的使用也可能存在歧义,但在这种情况下,解析器可以判断它不是,因为先前在方法中没有分配名为 blogs 的局部变量(如果曾经有一个裸露的 blogs 将具有该局部变量的值,如果您的意思是方法调用,则 self.blogs 是必需的。

至于使用@blogs =而不是self.blogs =,在这种情况下它会产生相同的效果,但是一个细微的区别:如果你稍后重新定义 blogs= 方法以获得其他效果(例如,将消息写入日志),对 self.blogs = 的调用将获取这些更改,而直接访问则不会。在极端情况下,如果您重新定义 blogs= 以将值存储在数据库中而不是实例变量中,@blogs = 甚至不会再 similar (尽管显然,基础设施的这种重大变化可能会在 class 内部产生连锁反应,无论如何)。

为了回答你的问题,我们必须揭示 attr_accessor 的真实本质。

class Foo
  attr_accessor :bar
end

完全等同于

class Foo
  def bar
    @bar
  end

  def bar=(value)
    @bar = value
  end
end

可以看到attr_accessor :bar定义了两个实例方法Foo#barFoo#bar=访问一个实例变量@bar.

然后让我们看看您的代码。

initialize中的

self.blogs = []实际上是调用方法User#blogs=,并通过它设置实例变量@blogs为空数组。可以写成 self.blogs=([]) 但是很吵,不是吗?顺便说一句,你不能在这里省略 self. 否则它只是设置一个局部变量。

blogs << added_blog 调用 User#blog 方法 returns @blogs 的值。它也可以写成 self.blogs().push(added_blog),但又不是 rubyish。可以省略self.因为User#add_blog中没有名为blogs的局部变量,所以ruby回退调用实例方法.

self.blogs = blogs.sort_by { |blog| blog.date }.reverse 混合调用 User#blogs=User#blogs.