返回首页 - Notes - 2016

Ruby 元编程(第2版)读书笔记


对象模型

requireload 的区别:

  1. load 用于加载代码,require 用于导入类库
  2. require 对每个文件只加载一次,而 load 每次调用时都会再次运行所加载的文件
  3. load 有副作用,常量(包括类)有可能污染当前程序的命名空间,load('xxx.rb', true) 才约等于 require 'xxx.rb'

方法查找口诀:向右一步,再向上

includeprepend 的异同:

  1. 共同点是,都将模块加入包含者的祖先链
  2. 不同点是,include 引入的模块在包含者的上面,prepend 引入的模块在包含者的下面

每个模块都只会引入一次,如果该模块已经存在于祖先链,则再次引入不会有任何影响

私有规则:

  1. 如果调用方法的接收者不是自己,那就必须明确指明接收者
  2. 私有方法只能通过隐性的接收者调用

顶层上下文的 selfmain 对象

在类和模块的定义中,在任何方法定义以外,self 就是类或模块本身

refine 和打开类的异同:

  1. 两者都可以打开类定义,给类添加新的方法
  2. 打开类全局有效,而 refine 只在 using 以后并在 using 作用域以内才有效

小结:


方法

动态派发:person.hello('Ruchee')person.send(:hello, 'Ruchee') 等价 可以用 send 调用任何方法,包括私有方法 可以用 public_send 限制对私有方法的调用

动态方法:使用 define_method 来定义方法

幽灵方法:使用 method_missing 截取所有未定义的方法调用

动态代理:用幽灵方法捕获方法调用,并转发给另外一个对象

每次覆写 method_missing 方法时,最好也同时覆写 respond_to_missing? 方法,以使得可以用 respond_to_missing? 来正确检测是不是存在某幽灵方法

对于方法,找不到时会被 method_missing 截取,而对于常量,找不到时同样会被一个叫 const_missing 的方法截取

白板类:拥有极少方法的类,可以避免祖先链中存在同名方法而导致 method_missing 调用不到的问题

Module#undef_methodModule#remove_method 的异同:

  1. 相同点都是删除方法
  2. undef_method 删除所有的方法,包括继承来的
  3. remove_method 只删除自己的方法,继承来的方法保留

代码块

可以用 block_given? 检测方法调用是否有传递代码块,代码块也就是闭包

全局变量可以在任何作用域中访问和修改

三个作用域门:classmoduledef

穿越作用域门:

上下文探针:

延迟执行:将代码块转成 proc 存储,后续再用 Proc#call 调用执行

创建 proc 的几种方法,以下各代码段作用等价

inc = Proc.new { |x| x + 1 }
puts inc.call(10)

inc = lambda { |x| x + 1 }
puts inc.call(10)

inc =*>x { x + 1 }
puts inc.call(10)

def create_proc (&block)
  block
end
inc = create_proc { |x| x + 1 }
puts inc.call(10)

def test (&inc)
  inc.call(10)
end
puts test { |x| x + 1 }

def test
  yield 10
end
puts test { |x| x + 1 }

class Test
  def inc (x)
    x + 1
  end
end
inc = Test.new.method :inc
puts inc.call(10)

定义方法时,最后一个参数以 & 打头可以将传递给该方法的代码块转成 proc;而调用方法时,在保存 proc 的变量名前加 & 可以将 proc 转回代码块

ProcLambda 的区别:

ProcLambda return 的差异代码示例:

def test
  l =*> { return 10 }
  l.call * 2
end
puts test  # 输出 20

def test
  p = Proc.new { return 10 }
  p.call * 2  # 这一行代码压根执行不到,前一行就已经返回了
end
puts test  # 输出 10

可以用 Method#unbindModule#instance_method 将一个方法变成自由方法,也可以用 UnboundMethod#bindModule#define_method 再次将自由方法绑定到某个对象


类定义

Module#class_eval 可以在不使用 class 关键字的情况下修改当前类

Module#module_evalModule#class_eval 的别名

class_evalmodule_eval 同样有孪生方法 class_execmodule_exec

类变量和类实例变量的区别:

单件方法:在单个对象上定义,且只对单个对象生效的方法

可以使用 def obj.xxx 定义单件方法,也可以使用 Object#define_singleton_method 方法来定义,可以用 Object.singleton_methods 获取某个对象全部的单件方法

类方法的实质:类方法其实就是一个类的单件方法

单件类:

单件类的超类是该单件类所归属类的超类的单件类,正因为如此,子类才可以调用到父类的类方法,因为类方法就是类的单件方法,保存在单件类之中,而方法调用会先查找单件类

定于类方法的三种方法:

def Hello.hello
  # xxx
end

class Hello
  def self.hello
    # xxx
  end
end

class Hello
  class <<self
    def hello
      # xxx
    end
  end
end

includeextend

extend 实例:

module HelloModule
  def hello
    "Hello, World"
  end
end

class HelloClass
  # 写法1
  class <<self
    include HelloModule
  end

  # 写法2
  extend HelloModule
end

puts HelloClass.hello

编写环绕别名的三个步骤:

  1. 给方法定义一个别名
  2. 重定义这个方法
  3. 在新方法中调用老方法

编写代码的代码

Binding:一个用对象表示的完整作用域,可以用 Kernel#binding 方法捕获当前作用域,然后可以通过 eval 方法在这个绑定对象所携带的作用域中执行代码

TOPLEVEL_BINDING:表示顶级作用域的绑定对象,可以在程序的任何地方访问到

全局变量 $SAFE 用于控制安全级别,默认为 0,可设置值为 03

Object#instance_variable_setObject#instance_variable_get 可用来操作实例变量


date:2016-04-10