天天看點

(cljs/run-at (JSVM. :all) "細說函數")

 作為一門函數式程式設計語言,深入了解函數的定義和使用自然是十分重要的事情,下面我們一起來學習吧!

定義文法

示例

其實<code>defn</code>是個macro,最終會展開為<code>fn</code>這種定義方式。是以後面的均以<code>fn</code>這種形式作說明。

注意:

Lambda表達式的函數體隻允許使用一個表達式,是以要通過special form<code>do</code>來運作多個表達式;

入參symbol為<code>%1,%2,...%n</code>,當有且隻有一個入參時可以使用<code>%</code>來指向該入參。

 Symbol和集合均支援附加metadata,以便向編譯器提供額外資訊(如類型提示等),而我們也可以通過metadata來标記源碼、通路政策等資訊。

 對于命名函數我們自然要賦予它Symbol,自然就可以附加中繼資料了。

 其中附加<code>:private</code>和<code>defn-</code>定義函數目的是一樣的,就是将函數的通路控制設定為private(預設為public),但可惜的是cljs現在還不支援<code>:private</code>,是以還是要用名稱來區分通路控制政策。

示例:

若隻打算設定document string而已,那麼可以簡寫為

雖然cljs隻支援<code>:doc</code>

 cljs為我們提供強大無比的入參解構能力,也就是通過聲明方式萃取入參

通過<code>&amp;</code>定義可變入參,可變入參僅能作為最後一個入參來使用

 通過組合可變入參和參數解構,我們可以得到命名入參

 Multi-Arity函數中我們可以通過入參數目來調用不同的函數實作,但有沒有一種如C#、Java那樣根據入參類型來調用不同的函數實作呢?clj/cljs為我們提供Multimethods這一殺技——不但可以根據類型調用不同的函數實作,還可以根據以下内容呢!

類型

屬性

中繼資料

入參間關系

 想說"Talk is cheap, show me the code"嗎?在看代碼前,我們先看看到底Multimethods的組成吧

1.dispatching function

 用于對函數入參作操作,如擷取類型、值、運算入參關系等,然後将傳回值作為dispatching value,然後根據dispatching value調用具體的函數實作。

2.method

 具體函數實作

3.hierarchy object

 存儲層級關系的對象,預設情況下所有相關的Macro和函數均采用全局hierarchy object,若要采用私有則需要通過<code>(make-hierarchy)</code>來建立。

還是一頭霧水?上示例吧!

示例1 —— 根據第二個入參的層級關系

示例2 -- 根據第一個入參的值

示例3 -- 根據兩入參數值比較的大小

 删除method

 先對dispatching value和method的dispatching-value進行<code>=</code>的等于操作,若不比對則對兩者進行<code>isa?</code>的層級關系判斷操作,就這樣周遊所有注冊到該multimethod的method,得到一組符合的method。若這組method的元素個數有且僅有一個,則執行該method;若沒有則執行<code>:default</code> method,若還是沒有則抛異常。若這組method的元素個數大于1,且沒有人工設定優先級,則抛異常。

 通過<code>prefer-method</code>我們可以設定method的優先級

 層級關系相關的函數如下:

上述函數當省略<code>h?</code>時,則操作的層級關系存儲在全局的hierarchy object中。

注意:層級關系存儲在全局的hierarchy object中時,Symbole、Keyword均要包含命名空間部分(即使這個命名空間并不存在),否則會拒絕。

另外還有<code>parent</code>、<code>ancestors</code>和<code>descendants</code>

 通過<code>(make-hierarchy)</code>可以建立一個用于實作局部層級關系的hierarchy object

注意:局部層級關系中的Symbol和Keyword是可以包含也可以不包含命名空間部分的哦!

 對于動态類型語言而言,當入參不符合函數定義所期待時,是将入參格式化為符合期待值,還是直接報錯呢?我想這是每個JS的工程師必定面對過的問題。面對這個問題我們應該分階段分子產品來處理。

開發階段,對于核心子產品,讓問題盡早暴露;

生産階段,對于與使用者互動的子產品,應格式化輸入,并在背景記錄跟蹤問題。

 而clj/cljs函數中的<code>condition map</code>就是為我們在開發階段提供對函數入參、函數傳回值合法性的斷言能力,讓我們盡早發現問題。

 在pre-exprs中我們可以直接指向函數的入參,在post-exprs中則通過<code>%</code>來指向函數的傳回值。

 現在我們可以安心把玩函數了,oh yeah!