`instance_eval` 和范围

`instance_eval` and scopes

我有以下代码:

class A
  def self.scope
    yield
  end
  def self.method_added method
    self.instance_eval %{
      # do something involving the added method 
    }
  end
end

class B < A
  scope do 
    def foo
    end
  end
end

当触发 method_added 钩子时,instance_eval 运行 中的代码是否与添加的方法在同一范围内?或者,它会在运行之外吗?

其中涉及哪些注意事项和陷阱?

让我们一探究竟!

class A
  def self.scope
    yield
  end
  def self.method_added method
    puts "In method_added, method = #{method}, self = #{self}" 
    instance_eval 'puts "In instance_eval, method = #{method}, self = #{self}"'
  end
end

class B < A
  scope do
    puts "In scope's block, self = #{self}" 
    def foo
    end
  end
end

  # In scope's block, self = B
  # In method_added, method = foo, self = B
  # In instance_eval, method = foo, self = B

请注意,在 self.instance_eval 中不需要 self.

您的 scope 方法基本上是空操作。当您将一个块传递给产生的方法时,该块将在 current 范围内进行评估。观察:

class A
  def self.scope
    yield
  end
end

A.scope { p self }
# main

由于块中没有产生任何内容,并且 yield 的 return 值也没有做任何事情,块中的任何代码 运行 都将具有相同的效果 运行 在 scope 块之外。

然而,instance_eval 并非如此。当 instance_eval 运行 是一个块时,块中的 self 被设置为接收者(而不是块范围内的任何 self )。像这样:

class A
end

A.instance_eval { p self }
# A

但请注意,这意味着 self.instance_eval { ... } 也是一个花哨的空操作,因为您正在将块的 self 更改为块外的相同 self

所以你的代码等同于:

class A
  def self.method_added method
    # do something involving the added method 
  end
end

class B < A
  def foo
  end
end