Ruby 手册 | 11 - Ruby 语言的动态特性

289 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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 相关方法获取变量时,实例变量一定是包含 @ 字符的形式,否则会报错。