《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 可用來操做實例變量

相關文章
相關標籤/搜索