前言
動態型別語言,少了靜態型別語言必須宣告變數型別的累贅,但也缺失了編譯時型別檢查和編譯時優化的好處。cljs雖然作為動態型別語言,但其提供Metadata讓我們在必要的時候可選擇地補充型別提示,以便提高程式碼可讀性和供編譯器優化使用。除了上述以外,Metadata還讓我們在不影響物件本質的前提下,附加額外資訊增強超程式設計能力。
Metadata附加的目標
首先要明確一點的是,Metadata不是任何物件/值都擁有的。只有如下的物件才可附加Metadata
- Symbol
- Var
- Collections(List,Map,Vector,Set)
- Record
- Type
meta
讀取Metadata
通過meta
我們可以獲取物件的Metadata,若沒有則返回nil
。
示例1:獲取Var的Metadata
(def a 1)
(meta #'a)
;;=> {:ns cljs.user, :name a, :file "<cljs repl>", :end-column 7, :source "a", :column 1, :line 1, :end-line 1, :arglists (), :doc nil, :test nil}
上述示例1中是(meta #'a)
而不是(meta a)
,前者是獲取Var的Metadata,而後者是獲取值1的Metadata,顯然後者是沒有Metadata的。
示例2:獲取Symbol的Metadata
(def a (with-meta 'a {:something "test"}))
(meta a)
;;=> {:something "test"}
通過with-meta
我們可以獲取附加了metadata的symbol'a
(注意作為入參的symbol'a
不會受到影響)。
with-meta
後期追加Metadata
上面我們已經看到with-meta
的使用示例了,下面我們再看看具體的函式簽名吧。
;; Returns an object of the same type and value as obj, with map m as its metadata.
(with-meta obj m)
值得注意的是,with-meta
會的返回值才會附加上metadata,而入參obj不會附加上metadata。因此需要用繫結來儲存結果,以便後續使用。
(def a
(with-meta obj m))
定義時附加Metadata
除了with-meta
後期追加外,很多時候我們是在定義時就已經可以明確metadata的了,那麼可以兩種形式定義metadata。
完整寫法
;; 定義Var的metadata
(def ^{:dynamic true, :tag "test"} a 1)
;; 讀取metadata
(meta #'a)
;; 定義Map的metadata
(def b ^{:something "test"} {:name 1})
;; 讀取metadata
(meta b)
縮寫——Metadata Reader
有時我們只想定義一兩個metadata,完整寫法顯然有些累贅,那麼我們就可以採用metadata reader的寫法,小清新一下。
(def ^:dynamic ^"test" a 1)
;;等價於(def ^{:dynamic true, :tag "test"} a 1)
縮寫是有限制,所以只能表達如下metadata
^:foo ;;=> ^{:foo true}
^"foo";;=> ^{:tag "foo"}
^foo ;;=> ^{:tag <value of foo>}
&esmp;至於其它metadata則還是要使用完整寫法處理。
內建的metadata
:dynamic ;; Boolean, 指定Var為動態繫結
:private ;; Boolean, 指定該Symbol的訪問控制為私有,預設為public
:doc ;; String, 設定document string
:test ;; Function,不帶入參的函式,單元測試函式
:tag ;; Class,指定Symbol所指向的Var的資料型別
另外編譯器會自動附加一下資訊到Var上。
:file ;; String
:line ;; Int
:name ;; Symbol
:ns ;; Symbol
:macro ;; Boolean,true表示是macro
:arglists ;; List<Vector>,每個Vector表示一個函式簽名
總結
今天就寫到這裡,下次繼續^^
尊重原創,轉載請註明來自:http://www.cnblogs.com/fsjohnhuang/p/7203927.html ^^肥仔John