携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
一、Ruby 的动态特性
Ruby 是一门动态解释型语言,动态特性是相对静态语言的,动态性为代码提供了运行时检查和修改程序的能力,能够让程序在运行时不断的调整适应不同的情况,甚至可以说动态性能让程序自己构造并执行代码,也就是元编程。
Ruby 的动态特性特现在以下几个方面:
- 动态执行字符串形式的代码
- 动态获得模块或者类中的常量和变量值
- 动态为类或者对象添加方法
- 对位置变量和方法的动态处理
- 动态删除定义
二、动态执行字符串形式代码
eval 是功能强大的动态执行函数,它能够编译并执行任何包含 Ruby 代码段的字符串。
在无法控制字符串内容的情况下,eval 是非常不安全的,恶意用户可能会利用它执行有害代码,Ruby 中还有 class_eval、module_eval 和 instance_eval,它们都具有动态执行字符串形式代码的能力
class_eval 是在类的上下文范围中奖字符串作为代码来执行,module_eval 是在类或者模块的上下文中执行,instance_eval 则是在实例的上下文范围中执行。
class String
def truncate(n)
self[0, n] + (self.size > n ? "...." : "")
end
for i in [5, 8, 10, 20]
module_eval "def truncate_#{i}
truncate #{i}
end
"
end
end
puts "stark".truncate(2)
puts "peter".truncate_20
puts "CaptainAmerican".truncate_10
执行上述代码,输出结果如下:
st....
peter
CaptainAme....
上述代码中为 String 类定义了一个 truncate 方法,用来截取指定长度的字符串并加上省略号,这个功能常用在标题的显示或者文章简介中。代码通过 module_eval 创建了 4 个实例方法,避免了使用时再次传递参数。
对象的 send 方法可以用作动态调用对象的方法,send 方法的参数为具体的方法名的字符串形式。
puts "tony".upcase # TONY
puts "tony".send("upcase") # TONY
三、动态获取类或模块中的方法、变量和常量值
方法的获取
Ruby 中的对象都具有 methods 方法,该方法返回的对象包含了所有公开方法的数组。
instance_methods 方法则返回所有公开的实例方法名,相应的 protected_methods 和 private_methods 则返回被 protected 和 private 关键字修饰的方法。
method_defined? 用于检查类中是否定义了指定的实例方法,response_to? 用于检查类是否能够响应某实例方法的调用。
puts Integer.methods # 返回数值对象的所有 public 修饰的 方法
puts Integer.instance_methods # 返回数值对象的所有 public 修饰的 方法
puts String.protected_methods # 返回字符串对象的所有 protected 修饰的 方法
puts String.method_defined?(:upcase) # true 表示 String 类中定义类 upcase 方法
puts "str".respond_to?(:upcase) # true 表示 str 字符串可以调用 upcase 实例方法
puts "str".respond_to?(:sqrt) # false sqrt 是数字对象调用的方法,str 字符串不可以调用,返回 false
常量的获取
const_get 方法可以根据常量的名称来获取模块或者类中的常量的值,例如获取 Math 对象中 PI 常量的值,除了 const_get 之外还有:
- const_defined?:用判断常量是否定义
- constants:获取全部常量值等
- const_set:用于定义一个常量,并返回常量的值
puts Math.const_get("PI")
puts Math.const_defined?(:P) # false
puts Math.const_set("P", 1000) # 1000
puts Math.const_defined?(:P) # true
puts Math.constants
执行上述代码,输出结果如下:
3.141592653589793
false
1000
true
DomainError
PI
E
P
const_get 方法还可以跟根据类的名称,创建类的实例,因为所有的类都继承自 Object 类,子类就相当于 Object 全局变量中的常量,只不过这个常量是一个类而已
class_name = "Array"
array_class = Object.const_get(class_name)
puts array_class.new
变量的获取
Ruby 中变量的获取的方法与常量获取的方法类似,也都包含了以下几个方法
- instance_variable_defined:判断指定的实例变量是否被定义
- instance_variable_set:设置一个实例变量
- instance_variables:获取所有的实例变量
- instance_variable_get:获取指定的实例变量
class Car
end
car = Car.new
puts car.instance_variable_defined?(:@color)
puts car.instance_variable_set("@color", "红色")
puts car.instance_variable_defined?(:@color)
puts car.instance_variables
puts car.instance_variable_get("@color")
执行上述代码,输出结果如下:
false
红色
true
@color
红色
instance_variable 相关方法获取变量时,实例变量一定是包含 @ 字符的形式,否则会报错。