我正在阅读Module文档,但似乎无法理解它们的差异,哪些应该在哪里使用.
有什么eval不同exec?
Mar*_*une 84
通过instance_{eval|exec}在你的问题中加入,我会回答你的问题.
所有变化都会{instance|module|class}_{eval|exec}改变当前上下文,即self:
class Array
p self # prints "Array"
43.instance_eval{ p self } # prints "43"
end
Run Code Online (Sandbox Code Playgroud)
现在为了差异.该eval版本接受一个字符串或块,而exec版本只接受一个块,但允许您将参数传递给它:
def example(&block)
42.instance_exec("Hello", &block)
end
example{|mess| p mess, self } # Prints "Hello" then "42"
Run Code Online (Sandbox Code Playgroud)
该eval版本不允许传递参数.它提供self了第一个参数,虽然我不能想到它的用途.
最后,module_{eval|exec}与相应的相同class_{eval|exec},但它们略有不同,instance_{eval|exec}因为它们def以不同的方式改变当前打开的类(即受影响的内容):
String.instance_eval{ def foo; end }
Integer.class_eval { def bar; end }
String.method_defined?(:foo) # => false
String.singleton_methods.include?(:foo) # => true
Integer.method_defined?(:bar) # => true
Run Code Online (Sandbox Code Playgroud)
所以obj.instance_{eval|exec}打开单身类obj,同时mod.{class|module}_{eval|exec}打开mod自己.
当然,instance_{eval|exec}可以在任何Ruby对象(包括模块)上使用,而{class|module}_*只能在Module(因此Classes)可用
要首先回答您的上一个问题,eval(在所有变体中)与exec 完全不同.exec $command将启动一个新进程来运行您指定的命令,然后在完成时退出.
class_eval并且module_eval有能力重新定义类和模块 - 甚至是那些你自己没有写的东西.例如,您可以使用类eval添加不存在的新方法.
Fixnum.class_eval { def number; self; end }
7.number # returns '7'
Run Code Online (Sandbox Code Playgroud)
class_eval可用于添加实例方法,instance_eval并可用于添加类方法(是的,该部分非常混乱).类方法就像Thing.foo- 你在字面上调用foo方法Thing.实例方法就像上面的例子,使用class_eval我已经number为每个实例添加了一个方法Fixnum.
好的,这就是*_eval方法类.exec方法类似,但它们允许您查看类并执行代码块,就好像它被定义为该类的方法一样.也许你有一个看起来像这样的类:
class Foo
@@secret = 'secret key'
@@protected = 'some secret value'
def protected(key)
if key == @@secret
return @@protected
end
end
end
Run Code Online (Sandbox Code Playgroud)
Foo如果你知道正确的密钥,那么这个类只是一个秘密值的包装器.但是,你可以通过在类的上下文中执行一个块来欺骗类给你秘密,如下所示:
Foo.class_exec { @@secret = 'i'm a hacker' }
Foo.protected('i'm a hacker') #returns the value of @@protected because we overwrote @@secret
Run Code Online (Sandbox Code Playgroud)
一般来说,使用ruby中的许多工具,您可以使用其中任何一个来解决许多问题.很多时候你甚至可能根本不需要,除非你想要修补你使用的某个类定义的类(尽管这会打开一大堆蠕虫).尝试用irb玩它们,看看哪个更容易找到.我个人并不像*_exec方法那样使用这些方法*_eval,但这是我个人的偏好.