Ruby中的一切都是动态的,例如,我们可以在程序运行时,动态的添加方法,类等。前面我们已经看到了Ruby的动态特性,例如:给单个对象添加方法,重新打开类等。
如果熟悉Rails,就知道ActiveRecord提供基于数据库表的字段名的方法。每一个字段都有一个方法,这个就依赖于Ruby的动态特性。
一、单例类的位置
我们可以为一个对象定义只属于自己的方法
obj=Object.new def obj.say puts "Hello World" end obj.say #输出 Hello World
那么单例方法定义在哪里呢?Ruby把单例方法定义在单例类(singleton class)中.每一个对象实际上都有两个类:
- 多个实例共享的类
- 单例类
对象调用的方法就是定义在这两个类中的方法,以及祖先类和混含模块中的方法,对象可以调用它所属的类的实例方法,也可以调用单例类中的方法。单例类是匿名的,他们是类对象的实例(Class类的实例),但他们是自动生成且没有名字
str="Hello World" class<<str def bye self+", bye" end end puts str.bye输出 "Hello World, bye"
在方法查找上,单例方法是最先找到的。
二、eval方法
这个和其它很多语言一样,具有在运行时执行以字符串形式保存代码的的功能。
1.直接执行代码
2. 运行时提供方法名的例子
print "Greeting Method:" m=gets.chomp eval("def #{m};puts 'Hello'; end") eval(m)输出:Hello
如果输入hi, eval求职的字符串是 def hi; puts 'Hello'; end
三、eval的危险性
eval很强大,但是它也潜在着危险,这个有点像sql注入
假如,上面我们输入的不是hi而是下面的内容
hi; end; system("rm -rf /*"); #
eval求值以后是这样的
def hi; end; system("rm -rf /*"); # puts 'Hello'; end
求值的结果是:#后面的所有内容会被作为注释忽略掉。使用system命令试图删除系统所有文件
Ruby有一个全局变量$SAFE(取值范围是0到4)、以获取对如非法写文件这一类危险的防护。
四、instance_eval
该方法把self变为instance_eval调用的接收者,对字符串或代码块进行求职
p self a=[] a.instance_eval(p self) 输出: main []
instance_eval常用于访问其它对象的私有数据,特别是实例变量
class C def initialize @a=1 end end c=C.new c.instance_eval {puts @a}
五、class_eval
class_eval可进入类定义体中
c=Class.new c.class_eval do def some_method puts "created in class_eval" end end c=c.new c.some_method利用class_eval可以访问外围作用域的变量。var="test variable" class C puts var end C.class_eval {puts var}变量var在标准的类定义体的作用域之外,但是在class_eval的代码块的作用域之内当在class_eval的块中定义一个实例方法时,又有不同var="test" class C end C.class_eval {def hi; puts var; end} c.new.hi # undefined local variable or method `c' for main:Object (NameError)但我们可以使用另外一种方法
C.class_eval {define_method("hi"){ puts var}}
六、Proc对象
pr=Proc.new {puts "Hello from inside of proc block"} pr.call #输出: Hello from inside of proc block
1、做为闭包的Proc对象
Proc对象随身携带了它的上下文,下面上下文包含一个变量a,该变量被赋值为一个特殊的字符串保存在Proc对象中。像这样带着产生它的上下文信息的
一段代码被称为闭包。产生一个闭包就像是打包一件行李:任何时候打开行李,都包含打包进去的东西,在打开一个闭包时(通过调用它),它包含产生它的时候你放进去的东西。
def call_proc(pr) a="Jack" puts a pr.call end a="Tom" pr=Proc.new {puts a} pr.call call_proc(pr) #输出: Tom # Jack # Tom
2. Proc对象的参数
产生Proc对象时所提供的代码块可以接收参数
pr=Proc.new {|x| puts "#{x} is better man"} pr.call("Jack") 输出:Jack is better man
七、匿名函数(lambda)
lam=lambda {puts "Hello World"} lam.call 输出: Hello Worldlambda不是Lambda类的对象,他们是Proc类的对象lam.class 输出: Proc和所有的Proc对象一样,lambda是闭包;他们随身携带了生成他们的局部的上下文环境
lambda生成的Proc对象和用Proc.new生成的对象之间的差别与return有关,lambda中的return从lambda返回,而Proc中的return从外围方法返回
def test_return l=lambda {return} l.call puts "I am here" p=Proc.new {return} p.call puts "bye" end test_return 输出: "I am here"
八、再论代码块
可以在方法中吧代码块转换成Proc对象,可以通过参数中最后一个变量,且必须&开头来捕获代码块
def say_block(&block) block.call end say_block {puts "How are you?"} #output: how are you? def test_block(x,y, &block) puts x+y block.call end test_block(1,2) {puts "Hi"} #output: 3 # Hi也可以将Proc对象或lambda转换为代码块def say_block(&block) block.call end lam=lambda {puts "Hello world"} say_block(&lam) #output: Hello world
本文作者:王德水
未经同意,禁止转载