Object#class
具体的类信息。[注]:#
表示实例方法,.
表示类方法。
Object#class.instance_methods(false)
显示public的实例方法。false表示只显示自己的方法,而不是继承来的。
Object#instance_variables
实例变量列表。和Java不同,Ruby中同一个类的不同对象可能有不同的实例变量列表,实例变量存放在对象中。
Object#methods
返回所有的公共实例方法。String.instance_methods == "aaa".methods
类的实例表方法表示它的对象的可以访问的方法。
Object#singleton_methods
可以得到对象的单件方法或类方法。通过true/false
来表示是否显示include的模块的方法。
Module.constants
表示当前环境所有的常量,包括定义的类名称。
Module#constants
表示当前实例的常量,例如A.constants
。由于A是一个Class的变量,Class继承自Module,所以A也就有了constants实例方法。
Module#ancestors
可以获取类的父类列表(包括模块),模块会按照声明的顺序正好在声明类的上一层。可以根据这个了解方法调用的路径。
self
标识调用方法的对象,在类定义中self
表示类自己。
Object#send(:method,args…)
可以直接调用对象的方法,这个方法可以调用私有方法,如果为了隐私可以使用public_send
。
Module#define_method
方法可以给类定义对象。
方法存放在类里,这里的可能是正常的类也可能是eigenclass,每个对象都有一个eigenclass,包括类自身。实例方法存放在类中,单件方法存放在eigenclass中,类方法是存放在类的eigenclass中。
obj是变量,Object、Class、String等是常量,它们也都是Class.new出来的变量。在Ruby中所有对象均是类,也都是对象。只要搞清楚当前执行环境的self、当前类就好理解很多了,self是方法的调用主体,当前类是方法定义的主体。
Object中包含methods等方法的定义,Module中包含instance_methods等方法的定义。由于类如String,MyClass都是Class的实例,所以都可以访问instance_methods,也由于这些类是Class的实例,所以他们可以使用Class的实例方法如new等来定义自己实例对象。上面也说明Object是为实例对象服务的,里面有很多实例方法。而Module、Class是为类服务的,里面有很多类定义相关的方法。
load('load.rb',true)
中load.rb中的变量会在作用域之外,我们看不到。但是常量如果不加true会影响现有的作用域,如果仅仅是要执行的结果可以加true,如果还需要常量定义,可以不加或是使用require。
可以通过打开类冲定义方法、新增方法等:
#打开类实例
class String
def to_alphanumberic
gsub /[^\w\s]/, ''
end
end
- 查询自身(自身的单件类中是否有此方法)
- 查询自身所属的类的实例方法
- 查询自身所属类的父类的实例方法
- 找不到方法则执行method_missing方法
当某个方法沿着继承链找不到时会调用method_missing
方法。核心库delegate
动态代理就利用了这个特性,很多适配器库也使用了这个特性。
使用method_missing
的问题:
- 方法可能导致死循环,使用时应注意。可以采用白名单和super来处理。
- 它比平常方法要慢1倍左右。
- 当一个幽灵方法和真实方法冲突时,比如继承自Object的方法,真实方法会胜出。这时可以使用白板类,这个类比Object类的方法还要少。ruby1.9之后可以直接继承Basic_Object来变成白板类。
class BlankSlate
#在一个白板类中隐藏名为给定name的方法
#但不隐藏instance_eval方法或任何一“__"打头的方法
def self.hide(name)
if instance_methods.include?(name.to_s) and
name !~ /^(__|instance_eval)/ #|instance_method 可能需要加上
@hidden_methods ||={}
@hidden_methods[name.to_sym] = instance_method(name)
undef_method name
end
instance_methods.each { |m| hide(m)}
#...
end
另:Module#const_missing()
当某个常量找不到时会调用这个方法。如果是在具体类或是Object中定义,则这个类的实例或是所有对象都可以使用此方法。
在代码中可以通过{}
或是do...end
关键字来传递块。在代码中可以使用yield(a,b)
来调用块。方法中可以通过Kernel#block_given?
方法判断当前是否传递了块。
def method2
(1..10).each do |x|
yield(x)
end
end
method2 do |x|
puts x if x != 4
break if x == 4
end
#这里的break会对method2中的each生效,打印结果为1,2,3
method2 do |x|
y ||= 1
y= y + 1
puts y
end
#会一直打印2,说明y每次都被初始化。
Ruby中有4中方法可以打包代码以备后用:
- 使用块(块不是对象);
- 使用Proc。(Proc是块转换的对象)
- 使用lambda。lambda会校验参数数目。
- 使用方法。
代码 + 绑定 = 块。可以通过binding方法获取当前绑定。eval方法可以指定binding。
块转换为Proc的方法:1.Proc.new
; 2.lambda()
3.proc()
.4.&
操作符。然后使用#call()
方法执行。
def math(a,b)
yield(a,b)
end
def teach_math(a,b,&operation)
puts "Let’s do the math:"
puts math(a,b, &operation)
end
teach_math(2,3) {|x,y| x+y}
操作符如+
、-
的类型是Proc,可以直接使用call
来执行,再加上&
可以把Proc转换为块。
Ruby的作用域没有嵌套的概念,当作用域切换时只能看到新的作用域。有3个方式可以重新定义作用域:类定义、模块定义、方法定义。
扁平作用域:Kernel#instance_eval
可以把执行它的对象作为块的self对象,从而块可以访问对象中的所有实例变量、私有方法等。这种块也是扁平作用域,也称为”上下文探针”。#instance_exec
功能类似,但是它可以给块传参数。
其实扁平作用域就是利用Class.new
、class_eval
、instance_eval
等在不切换作用域的情况下完成类定义、方法定义、在对象内部执行代码等功能。
使用Module#class_eval
在不知道类名的时候打开类,定义方法。它会把当前类切换为当前类,同时设置了self变量。Ruby总是会跟踪当前类,这个和self是不同的,它是self的类。
instance_eval
也会修改当前类,它修改的是接收者的eigenclass.
#method1只对a有效。
a.instance_eval do
def method1; “ok”;end
end
元类是对象自身的类,可以通过下面的方法获取它。
class BasicObject
def eigenclass
class << self; self ; end
end
end
对象元类的父类是它所属的类。obj.eigenclass.superclass = obj.class
类的元类的父类是类的父类的元类(为了支持类方法继承)。特殊的BasicObject的元类是Class。Object.eigenclass.superclass = BasicObject.eigenclass = Class
单件singleton方法:
str ="hello str"
def str.title?
self.upcase == self
end
也可以通过下面这种方式定义:
class << an_object
#这里就是eigenclass的作用域了
end
irb中定义的方法,是作为Object的私有方法存在的。因为irb的self对象是main,是Object的一个实例对象;irb中当前类是Object。
def method1
puts "method1"
end
"aa".send :method1
类宏:本质是类中的类方法。
#例子:声明取消的方法:
class Book
def subtitle
#...
end
def self.deprecate(old_method, new_method)
define_method(old_method) do |*args, &block|
warn "Warning: #{old_method} is deprecated.Use #{new_method}"
send(new_method, *args, &block)
end
end
deprecate :title2, :subtitle
end
include模块:在类中调用Module#include
方法包含其他模块,模块中定义的方法会成为类的实例方法(通过建立ancestors关系)。如果在类的eigenclass中include模块,模块中定义的方法会成为这个类的类方法,这样其实等效于调用Object#extend
方法。
类扩展混入:通过使用钩子方法(ruby中很多钩子方法,包括继承时,包含时调用的)。
Module M
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def my_method
'a class method'
end
end
end
class C
inlcude M
end
C.my_method #=>"a class method"
空指针保护: a ||= []
参数数组: 使用*号来将多个值转换为数组。
*a = 1, 2, 3 #=> a = [1, 2, 3]
具名参数:method(username: "hello", age: 13)
上述值会作为最后一个参数的值,最后一个参数会是hash数组。当默认参数、边长参数、具名参数混用时需要自己进行参数解析。
符号到Proc:把一个符号转换为调用单个方法的代码块:
#这里会自动调用Symbol#to_proc方法
#下面是这个方法的源码
class Symbol
def to_proc
Proc.new {|x| x.send(self)}
end
end
# &符号可以作用于任何对象,会调用它的.to_proc方法.
[1, 2, 3, 4].map(&:even?) #=>[false, true, false, true]
环绕别名:通过alais关键字定义方法别名,然后把原来方法重定义。
class String
alias :real_length :length
def length
real_length > 5 ? 'long' : 'short'
end
end
"war and peace".length #=> 'long'
"war and peace".real_length #=> 13