Ruby 通过 eval 可以将字符串当做代码来执行,而执行环境是通过 binding 进行绑定的;除此之外,Ruby 还有另外两个方法:instance_eval 和 class_eval, 用来执行代码块。后者的执行上下文与调用者相关。
instance_eval: 调用者必须是实例 instance;
class_eval: 调用者必须是类 class;
下面的例子帮助识别两个方法的区别:
[1] pry(main)> class Hello
[1] pry(main)* def self.say_c
[1] pry(main)* puts "Hello self.say"
[1] pry(main)* end
[1] pry(main)* def say_i
[1] pry(main)* puts "Hello lsay"
[1] pry(main)* end
[1] pry(main)* end
在 pry 交互环境中,先定义一个 Hello 类,有两个方法,一个类方法 say_c, 一个实例方法 say_i。
[2] pry(main)> ls Hello
Hello.methods: say_c
Hello#methods: say_i
分别通过 Hello.instance_eval 和 Hello.class_eval 定义两个新方法: foo_i, foo_c
[4] pry(main)> Hello.instance_eval do
[4] pry(main)* def fool_i
[4] pry(main)* puts "Hello instance_eval foo_i"
[4] pry(main)* end
[4] pry(main)* end
=> nil
[5] pry(main)> Hello.class_eval do
[5] pry(main)* def foo_c
[5] pry(main)* puts "Hello class_eval foo_c"
[5] pry(main)* end
[5] pry(main)* end
=> nil
查看 Hello 的方法变化:
[6] pry(main)> ls Hello
Hello.methods: foo_i say_c
Hello#methods: foo_c say_i
有上面的结果可见,instance_eval 代码块中定义的方法变成了 Hello 的类方法,而 class_eval 代码块中
定义的方法称为了 Hello 类的实例方法。这里有点绕,跟语义上边理解起来的相反。
[7] pry(main)> h1 = Hello.new
=> #<Hello:0x007ff978fc5458>
[8] pry(main)> h1.instance_eval do
[8] pry(main)* def cry
[8] pry(main)* puts "h1 cry"
[8] pry(main)* end
[8] pry(main)* end
=> nil
[9] pry(main)> ls h1
Hello#methods: foo_c say_i
self.methods: cry
h1 为 Hello 类的实例,h1.instance_eval 中定义的方法成为了 h1 的单例方法(类似于java中类的静态方法),也就是说只有 h1 能够调用的方法。
总结:
- h1 不能调用 class_eval;
- h1 调用 instance_eval 的作用域为对象实例本身;其中定义的方法为单例方法;
- Hello 调用 class_eval 的作用域为类本身;其中定义的方法为实例方法;
- Hello 调用 instance_eval 的作用域为 class 对象,其中定义的方法为类方法;