为什么你可以在'initialize'中调用instance_eval(而不是class_eval)?

uzo*_*uzo 2 ruby

  class Observer
    def initialize(&block)
      instance_eval(&block) if block_given?
    end
  end 
Run Code Online (Sandbox Code Playgroud)

我想知道在这里假设与'initialize'一起使用的块的类型.

由于调用了instance_eval,这意味着在Observer类的上下文中计算块.

为什么它会这样做,而不是比如说class_eval,以及在类的上下文中评估块的结果可能是什么?

另外,如何调用它?

Pes*_*sto 9

首先,你不能做这样的事情:

class Observer
  def initialize(&block)
    class_eval(&block) if block_given?
  end
end
Run Code Online (Sandbox Code Playgroud)

因为class_eval没有为实例定义Observer.它定义在Module(从中Class下降).我们稍后会回来class_eval.

使用上述习语的原因通常是允许块初始化:

x = Observer.new do
  add_event(foo)
  some_other_instance_method_on_observer
  self.some_attribute = something
end
Run Code Online (Sandbox Code Playgroud)

另外,您可以将方法添加到类的给定实例:

foo = Observer.new do
  def foo
    'foo'
  end
end

foo.foo  # => "foo"
Run Code Online (Sandbox Code Playgroud)

没有instance_eval:你可以完成大致相同的事情:

class Foo
  def initialize
    yield self if block_given?
  end
end

foo = Foo.new do |x|
  x.add_event(foo)
  x.some_other_instance_method_on_observer
  x.self.some_attribute = something
end
Run Code Online (Sandbox Code Playgroud)

但这并没有让你能够添加方法.如果你这样做:

foo = Foo.new do
  def foo
    'foo'
  end
end

foo.foo  # => "foo"
Run Code Online (Sandbox Code Playgroud)

它似乎工作,对吧?但你实际做的是将foo方法添加到一切,因为self设置为"主"对象.它等同于简单地定义块外的方法.它们作为实例方法添加Object,因此它们可以处理所有事情.

现在,按照承诺,简要回归class_eval.你可以这样做:

class Observer
  def initialize(&block)
    class.class_eval(&block) if block_given?
  end
end
Run Code Online (Sandbox Code Playgroud)

但是你打开了整个班级:

x = Observer.new { def foo; 'foo'; end }
x.foo                                      # => "foo"
y = Observer.new
y.foo                                      # => "foo"
Run Code Online (Sandbox Code Playgroud)

这通常不是我们想要做的.另外,self将是班级,而不是实例.这使得它对于如上所述的块初始化是无用的.