首先,很抱歉,我在 Ruby 中的 class_eval 和 module_eval 方法 中漏了介绍另一个方法 instance_eval。
其实,Ruby 中除了可以在 「 运行时 」 ( on-the-fly ) 向类或模块添加属性或方法之外,还可以向特定的实例添加方法,这种机制是通过 BasicObject#instance_eval 方法来实现的。
为什么没介绍呢 ?
因为这个方法根本不在 module 中。
添加方法
Ruby 中,当我们想要向实例添加方法时,我们可以使用 BasicObject#instance_eval 方法。此方法接受一个字符串或 「 块 」 作为参数
array_second = <<-RUBY
def second
self[1]
end
RUBY
a = [1, 2, 3]
a.instance_eval(array_second)
a.second # returns: 2
str = "ruby.devscoop.fr"
str.instance_eval do
def /(delimiter)
split(delimiter)
end
end
str / '.' # returns: ["ruby", "devscoop", "fr"]
上面的代码中,调用 a.instance_eval(array_second) 方法会将 second 方法添加到变量 a 中 - 这是 Array 的一个实例 - 传递的字符串参数将会在实例 a 的上下文中执行
而将一个块作为参数调用 str.instance_eval 方法,会在实例 str 的上下文中运行块的内容 - str 是 String 的一个实例。
访问内部变量
instance_eval 方法的另一个特性是它能够访问内部变量。
class User def initialize(email) @email = email end end u = User.new('no-reply@twle.cn') u.instance_eval('@email') # returns: "no-reply@twle.cn" u.instance_eval { @email } # returns: "no-reply@twle.cn"
上面的代码中,我们将内部变量名 @email 的字符串形式作为参数传递给 u.instance_eval 来访问内部变量 @email
这是可能的,因为 instance_eval 是在接收器对象的上下文中执行的 - 在本例中是 u 变量
这意味着作为参数传递的所有代码都将在名为 u 的 User 实例的范围内执行
绕过方法访问控制
通过使用 instance_eval,可以从此外部调用给定对象的私有或受保护方法
class User def initialize(email) @email = email end private def secret_key 'XX-XXXX-XXXX-XXXX' end end u = User.new('no-reply@twle.cn') u.instance_eval('secret_key') # returns: "XX-XXXX-XXXX-XXXX" u.instance_eval { secret_key } # returns: "XX-XXXX-XXXX-XXXX"
上面的代码中,我们将内部方法名 secret_key 的字符串形式作为参数传递给 u.instance_eval 来访问内部方法 secret_key