instance_eval
并class_eval
允许您执行一大段代码。那你可能会说什么?老式的eval
可以做到这一点。但是instance_eval
并class_eval
接受代码块的块参数。所以代码块不需要是字符串。还允许接收器(instance_eval
与class_eval
旧的不同eval
)。因此,您可以在类对象甚至实例对象上调用这两个现代方法。
class A
end
A.instance_eval do
# self refers to the A class object
self
end
a = A.new
a.instance_eval do
# self refers to the a instance object
self
end
还要记住,在 ruby 中,如果我们调用一个没有接收器的方法,那么该方法将被调用 on self
,在instance_eval
块中是我们调用的对象instance_eval
。实例变量在 ruby 中是私有的。您不能在定义它们的类之外访问它们。但是由于实例变量存储在 中self
,我们可以在其中访问它们instance_eval
(这同样适用于不能用接收器调用的私有方法):
class A
def initialzie
@a = “a”
end
private
def private_a
puts “private a”
end
end
a = A.new
puts a.instance_eval { @a }
# => “a”
puts a.instance_eval { private_a }
# => “private a”
我们还可以在instance_eval
和中为接收者添加方法class_eval
。在这里,我们将其添加到instance_eval
:
class A
end
A.instance_eval do
def a_method
puts “a method”
end
end
A.a_method
# => a method
现在想想我们刚刚做了什么。我们使用instance_eval
,在其中定义了一个方法block
,然后在类对象本身上调用该方法。这不是类方法吗?如果您愿意,可以将其视为“类”方法。但是我们所做的只是在instance_eval
块中的接收器上定义一个方法,而接收器恰好是A
. 我们可以很容易地在实例对象上做同样的事情:
a.instance_eval do
def a_method
puts "a method"
end
end
a.a_method
# => a method
它的工作原理是一样的。不要将类方法视为其他语言中的类方法。它们只是在 上定义的方法self
,而self
恰好是一个类对象(从Class.new
as in扩展class A end
)。
但我想把这个答案比接受的答案更深入一点。instance_eval
实际上将您放入其中的方法粘贴在哪里?他们进入singleton
接收者的班级!一旦您instance_eval
在接收器上调用,ruby 解释器就会打开singleton_class
并将块中定义的方法放在 thissingleton_class
中。就像extend
在类中使用一样(因为extend打开了单例类,并将传递给扩展的模块中的方法放入单例类中)!它打开了singleton_class
,它是继承层次结构的一部分(就在父类之前):A -> singleton_class -> Parent
现在有什么class_eval
不同?class_eval
只能在类和模块上调用。self
仍然指的是接收者:
class A
end
A.class_eval do
# self is A
self
end
但与 不同instance_eval
的是,当您在class_eval
块中定义方法时,它们将在类的实例而不是类对象本身上可用。使用class_eval
,方法不会添加到继承层次结构中的单例类中。相反,方法被添加到current class
接收者的!因此,当您在 中定义方法时class_eval
,该方法直接进入current class
,因此它成为实例方法。所以你不能在类对象上调用它;您只能在类对象的实例上调用它。