首先,很抱歉,我在 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