日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

(cljs/run-at (JSVM. :all) 一次说白DataType、Record和Protocol)

發布時間:2024/1/17 javascript 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (cljs/run-at (JSVM. :all) 一次说白DataType、Record和Protocol) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

?在項目中我們一般會為實際問題域定義領域數據模型,譬如開發VDOM時自然而言就會定義個VNode數據類型,用于打包存儲、操作相關數據。clj/cljs不單內置了List、Vector、Set和Map等數據結構,還提供deftype和defrecord讓我們可以自定義數據結構,以滿足實際開發需求。

定義數據結構從Data Type和Record開始

?提及數據結構很自然就想起C語言中的struct,結構中只有字段并沒有定義任何方法,而這也是deftype和defrecord最基礎的玩法。
示例

(deftype VNode1 [tag props]) (defrecord VNode2 [tag props])(def vnode1(VNode1. "DIV" {:textContent "Hello world!"})) ;; 或 (->VNode1 "DIV" {:textContent "Hello world!"})(def vnode2(VNode2. "DIV" {:textContent "Hello world!"})) ;; 或 (->VNode2 "DIV" {:textContent "Hello world!"}) ;; 或 (map->VNode2 {:tag "DIV", :props {:textContent "Hello world!"}})

?這樣一看兩者貌似沒啥區別,其實區別在于成員的操作上

;; deftype取成員值 (.-tag vnode1) ;;=> DIV ;; defrecord取成員值 (:tag vnode2) ;;=> DIV;; deftype修改成員值 (set! (.-tag vnode1) "SPAN") ;; 或 (aset vnode1 "tag" "SPAN") (.-tag vnode1) ;;=> SPAN;; defrecord無法修改值,只能產生一個新實例 (def vnode3(assoc vnode2 :tag "SPAN")) (:tag vnode2) ;;=> DIV (:tag vnode3) ;;=> SPAN

?從上面我們可以看到defrecord定義的數據結構可以視作Map來操作,而deftype則不能。
?但上述均為術,而背后的道則是:
在OOP中我們會建立兩類數據模型:1.編程領域模型;2.應用領域模型。對于編程領域模型(如String等),我們可以采用deftype來定義,從而提供特殊化能力;但對于應用領域模型而言,我們應該對其進行抽象,從而采用已有的工具(如assoc,filter等)對其進行加工,并且對于應用領域模型而言,一切屬性應該均是可被訪問的,并不存在私有的需要,因為一切屬性均為不可變的哦。

Protocol

?Protocol如同Interface可以讓我們實施面對接口編程。上面我們通過deftype和defrecord我們可以自定義數據結構,其實我們可以通過實現已有的Protocol或自定義的Protocol來擴展數據結構的能力。

deftype和defrecord在定義時實現Protocol

;; 定義protocol IA (defprotocol IA(println [this])(log [this msg]));; 定義protocol IB (defprotocol IB(print [this][this msg]));; 定義數據結構VNode并實現IA和IB (defrecord VNode [tag props]IA(println [this](println (:tag this)))(log [this msg](println msg ":" (:tag this)))IB(print ([this](print (:tag this)))));; 各種調用 (def vnode (VNode. "DIV" {:textContent "Hello!"})) (println vnode) (log vnode "Oh-yeah:") (print vnode)

注意IB中定義print為Multi-arity method,因此實現中即使是僅僅實現其中一個函數簽名,也要以Multi-arity method的方式實現。

(print ([this] (print (:tag this))))

否則會報java.lang.UnsupportedOperationException: nth not supported on this type: Symbol的異常

對已有的數據結構追加實現Protocol

?Protocol強大之處就是我們可以在運行時擴展已有數據結構的行為,其中可通過extend-type對某個數據結構實現多個Protocol,通過extend-protocol對多個數據結構實現指定Protocol。
1.使用extend-type

;; 擴展js/NodeList,讓其可轉換為seq (extend-type js/NodeListISeqable(-seq [this](let [l (.-length this)v (transient [])](doseq [i (range l)](->> i(aget this)(conj! v)))(persistent! v)))) ;; 使用 (map#(.-textContent %)(js/document.querySelector "div"));; 擴展js/RegExp,讓其可直接作為函數使用 (extend-type js/RegExpIFn(-invoke ([this s](re-matches this s))));; 使用 (#"s.*" "some") ;;=> some

2.使用extend-protocol

;; 擴展js/RegExp和js/String,讓其可直接作為函數使用 (extend-protocol IFnjs/RegExp(-invoke ([this s] (re-matches this s)))js/String(-invoke ([this n] (clojure.string/join (take n this)))));; 使用 (#"s.*" "some") ;;=> some ("test" 2) ;;=> "te"

?另外我們可以通過satisfies?來檢查某數據類型實例是否實現指定的Protocol

(satisfies? IFn #"test") ;;=> true ;;對于IFn我們可以直接調用Ifn? (Ifn? #"test") ;;=>true

reify構造實現指定Protocol的無屬性實例

(defn user[firstname lastname](reifyIUser(full-name [_] (str firstname lastname)))) ;; 使用 (def me (user "john" "Huang")) (full-name me) ;;=> johnHuang

specify和specify!為實例追加Protocol實現

specify可為不可變(immutable)和可復制(copyable,實現了ICloneable)的值,追加指定的Protocol實現。其實就是向cljs的值追加啦!

(def a "johnHuang") (def b (specify aIUser(full-name [_] "Full Name")))(full-name a) ;;=>報錯 (full-name b) ;;=>Full Name

specify!可為JS值追加指定的Protocol實現

(def a #js {}) (specify! aIUser(full-name [_] "Full Name"))(full-name a) ;;=> "Full Name"

總結

?cljs建議對數據結構進行抽象,因此除了List,Map,Set,Vector外還提供了Seq;并內置一系列數據操作的函數,如map,filter,reduce等。而deftype、defrecord更多是針對面向對象編程來使用,或者是面對內置操作不足以描述邏輯時作為擴展的手段。也正是deftype,defrecord和defprotocol讓我們從OOP轉FP時感覺更加舒坦一點。
?另外deftype,defrecord和protocol這套還有效地解決Expression Problem,具體請查看http://www.ibm.com/developerw...

尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohn... ^_^肥仔John

總結

以上是生活随笔為你收集整理的(cljs/run-at (JSVM. :all) 一次说白DataType、Record和Protocol)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。