一个简单的测试
// gem install test-unit --verbose
def to_word(s)
s.gsub(/\d/, '')
end
require 'test/unit'
class ToWordTest < Test::Unit::TestCase
def test1
assert_equal 'hello world', to_word('234234he345345l345345lo345345 wo345r4353ld')
end
end
在 class String 上添加方法
class String
def to_word
self.gsub(/\d/, '') # self 可以省略
end
end
require 'test/unit'
class ToWordTest < Test::Unit::TestCase
def test1
assert_equal 'hello world', '234234he345345l345345lo345345 wo345r4353ld'.to_word
end
end
Class
重复的 class
3.times do
class C
p 'hello'
end
end
验证只是 class 可以重复声明且会自动合并
class C
def x
p 'x'
end
end
class C
def y
p 'y'
end
end
z = C.new
z.x
z.y
自定义一个 class
class MyClass
def initialize
@v = 1
end
def add
@v += 1
end
end
obj = MyClass.new
p obj.class
p obj.add
p obj.instance_variables
p MyClass.instance_variables
p obj.methods
p MyClass.instance_methods
p obj.methods.grep(/add/)
证明,一个对象的实例变量储存在自身,一个对象的实例方法存储在其类身上
superclass (类似于 JS 的原型链)
p '----String----'
p String.superclass # Object
p Object.superclass # BasicObject
p BasicObject.superclass # nil
p String.is_a? Class
p '----Array----'
p Array.superclass # Object
p Array.is_a? Class
class MyClass; end
p MyClass.superclass # Object
p MyClass.is_a? Class
p 'class is a Class?'
p Class.is_a? Class
p Class.superclass # Module
# 模块是低配版的类,类是高配版的模块
模块是低配版的类,类是高配版的模块
常量
Const1 = 'root const'
module MyModule
Const1 = 'outer const'
class MyClass
Const1 = 'inner const'
p ::Const1 # 'root const'
end
end
p Const1 # 'root const'
p MyModule::Const1 # 'outer const'
p MyModule::MyClass::Const1 # 'inner const'
使用
::
打印出内部的常量,直接使用::
可以打印出根的常量
p '------------'
p MyModule.constants
使用 MyModule.constants 得到 MyModule 的内部的所有一级常量
Const1 = 'root const'
module MyModule
Const1 = 'outer const'
class MyClass
Const1 = 'inner const'
p 'Module.nesting'
p Module.nesting # [MyModule::MyClass, MyModule]
end
end
使用 Module.nesting 得到 当前作用域的层级结构
祖先链
class MyClass
def my_class
'my_class'
end
end
class MySubClass < MyClass # < 表示继承
end
obj = MySubClass.new
p obj.my_class
p MySubClass.ancestors # [MySubClass, MyClass, Object, Kernel, BasicObject] # 其中 Kernel 是祖先模块
module
模块和类非常非常像,只是模块不能继承
module M1
def my_methods
'M1#my_methods'
end
end
class C1
include M1
end
class A1 < C1
end
p A1.ancestors # [A1, C1, M1, Object, Kernel, BasicObject]
module M1
def my_methods
'M1#my_methods'
end
end
class C1
prepend M1
end
class A1 < C1
end
p A1.ancestors # [A1, M1, C1, Object, Kernel, BasicObject]
使用 include (上面增加)和 prepend(下面增加)
可以在类中引入模块,用来改变方法查找顺序,使得继承组合更加多样化
self (method 的查找方法)
self 是当前对象
class MyClass
def test_self
@var = 10
me_method
self
end
def me_method
@var += 1
end
end
obj = MyClass.new
p obj.test_self # #<MyClass:0x00000000051488b8 @var=11>
可以看到 @var 已经是 11 了,执行顺序:先找 obj 上的 test_self, 在MyClass 上找到,执行该方法,该方法声明了 @var 并执行 me_method 方法,注意调用该方法时,self已经变了,所以 me_method 才能给 @var 加一
特殊:顶级的 self
p self # main
p 出来的肯定是对象,为什么是 main,只需要改写其 to_s 和 inspect 方法即可,
o1 = {name: 'zch'}
p o1
p self
class Temp
def to_s
'temp'
end
def inspect
'temp'
end
end
p Temp.new
self 也可以是类
class MyClass2
p 'MyClass2'
p self # self 是类
def my_method
p self
end
end
私有方法
class MyClass
def public_method
'MyClass::public_method'
end
private
def private_method1
'MyClass::private_method1'
end
def private_method2
'MyClass::private_method2'
end
end
使用 private 分隔私有方法,非常机智,private 以上都是不是私有的,下面都是私有的
obj = MyClass.new
obj.public_method
obj.private_methods1 # 报错
obj.private_methods2 # 报错
调用 private 方法
class MyClass
def public_method1
self.private_method1 # 报错
end
def public_method2
private_method1
end
private
def private_method1
'MyClass::private_method1'
end
def private_method2
'MyClass::private_method2'
end
end
私有方法调用必须满足如下情况
- 必须在对象所在的类中调用
- 不能再前面加 self 字样(只能隐式指定 self)
动态调用方法
调用方法有两种
- obj.xx_method('hi')
- obj.send(:xx_method, 'hi')
class Notification
def notify(witch)
send "notify_#{witch}"
end
private
def notify_wx
p '通知到威信'
end
def notify_qq
p '通知到qq'
end
def notify_phone
p '通知到手机'
end
def notify_all
notify_wx
notify_qq
notify_phone
end
end
obj = Notification.new
obj.notify('qq')
注意 send 调用方法不分共、私有,上述可以直接
obj.send(:notify_qq, 'hi')
。
动态定义方法
class MyClass
x = :methods1
define_method x do |n|
n * 3
end
end
require 'test/unit'
class MyClassTest < Test::Unit::TestCase
def test_1
obj = MyClass.new
assert_equal 6, obj.methods1(2)
end
end
动态定义的好处:可以在运行时决定方法的名字,而不是写代码时
这在批量创建方法时非常有用
自定义未定义方法(狗头)
class MyClass;end
obj = MyClass.new
obj.xxx
调用
obj.xxx
, 一般来说调用一个未定义的方法,其他语言会直接报错,但是 ruby 不会,obj 会一直向上查找,直到调用BaseObject#method_missing
这个方法去报错
也就等同于obj.send(:method_missing, :xxx)
,这两个报错一模一样,即可验证
所以我们就可以直接覆盖 method_mssing
class MyClass
private def method_missing(method, *args)
p "method:#{method},参数:#{args}"
p '找不到方法啊,大哥'
end
end
obj = MyClass.new
obj.xxx(1,2,3)
这么做以后,就可以让一个对象响应任何一个方法
网友评论