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

对象模型

  • Module#constants 能够获取当前范围内全部的常量c#

  • Module.constants 获取当前程序中全部顶层的常量安全

  • Module.nesting 能够获得当前代码所在的路径ruby

  • ClassName.ancestors 能够获取类的祖先链闭包

requireload 的区别:ui

  1. load 用于加载代码,require 用于导入类库代理

  2. require 对每一个文件只加载一次,而 load 每次调用时都会再次运行所加载的文件code

  3. load 有反作用,常量(包括类)有可能污染当前程序的命名空间,load('xxx.rb', true) 才约等于 require 'xxx.rb'对象

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

includeprepend 的异同:作用域

  1. 共同点是,都将模块加入包含者的祖先链

  2. 不一样点是,include 引入的模块在包含者的上面,prepend 引入的模块在包含者的下面

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

私有规则:

  1. 若是调用方法的接收者不是本身,那就必须明确指明接收者

  2. 私有方法只能经过隐性的接收者调用

顶层上下文的 selfmain 对象

在类和模块的定义中,在任何方法定义之外,self 就是类或模块自己

refine 和打开类的异同:

  1. 二者均可以打开类定义,给类添加新的方法

  2. 打开类全局有效,而 refine 只在 using 之后并在 using 做用域之内才有效

小结:

  • 对象由一组实例变量和类的引用组成

  • 对象的方法存在于对象所属的类中

  • 类自己是 Class 类的对象

  • Class 类是 Module 的子类,类只是比模块多了 newsuperclass 方法

  • 类祖先链的最顶端是 BasicObject

  • 实例变量永远被认为是 self 的实例变量

方法

动态派发: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

穿越做用域门:

  • Class.new 穿越 class 做用域

  • Module.new 穿越 module 做用域

  • define_method 穿越 def 做用域

上下文探针:

  • instance_val 在一个对象的上下文中执行代码块

  • instance_execinstance_val 功能基本一致,但容许给代码块传入参数

延迟执行:将代码块转成 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 的区别:

  • lambda 方法建立的 Proc 称为 lambda,而用其余方式建立的则称为 proc(能够用 Proc#lambda? 检测是否是 lambda

  • lambda 里面的 return 仅从该 lambda 中返回,而 proc 里面的 return 倒是从定义该 proc 的做用域中返回

  • 在参数适应能力上,proc 适应能力更强,而 lambda 对传递的参数个数要求严格

  • 综合 return 和参数,lambda 的表现更像真实的方法:严格检查参数个数,只从本身的代码区域返回

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 获取某个对象所有的单件方法

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

单件类:

  • 保存一个对象全部的单件方法

  • 只有一个实例,不能被继承

  • 在类继承链中不可见,只能用 Object#singleton_classclass<< 来获取

  • 若是将单件类从继承链标识出来,它是在该对象所属类下方的,也就是对象的方法调用是先查找的单件类,再查找的类自己

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

定义类方法的三种方式:

def Hello.hello
  # xxx
end

class Hello
  def self.hello
    # xxx
  end
end

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

includeextend

  • include 是在类层面包含模块,所包含模块的方法将成为类的实例方法

  • extend 是在单件类层面包含模块,所包含模块的方法将成为单件类的实例方法,也就是类的类方法

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 可用来操做实例变量

相关文章
相关标签/搜索