前言
一個cljs文件定義一個命名空間,通過命名空間可以有效組織代碼,這是構建大型系統必備的基礎設施。本篇我們就深入理解cljs中的命名空間吧!
好習慣從"頭"開始
每個cljs文件首行非注釋的內容必定如下
(ns my-project.core)
而當前的cljs文件路徑為${project_dir}/src/my_project/core.cljs
,很明顯命名空間與源碼文件路徑是一一對應的,對應規則是-
對應_
,.
對應/
咯~
引入其他命名空間
要使用其他命名空間下的成員,那么必須先將其引入到當前命名空間才可以。但注意的是,默認情況下會自動引入cljs.core
這個命名空間,而且會將其成員注入到當前命名空間中。因此(ns my-project.core)
最后會編譯為等價于以下語句
;; 注意:cljs中并不支持:all這種引入,因此這面語句僅僅用于表達注入所有成員而已
(ns my-project.core
(:require [cljs.core :all]))
所以我們可以直接調用reduce
而不是cljs.core/reduce
。
我們沒可能只調用cljs.core
的成員吧,那到底如何引入其他命名空間呢?下面我們一一道來!
通過:require
1.直接引入
(ns my-project.core
(:require clojure.data))
;; 使用時需要指定成員所屬的命名空間
(clojure.data/diff 1 2)
2.注入成員到當前命名空間
; 將clojure.data/diff和clojure.data/Diff兩個成員注入到當前命名空間
(ns my-project.core
(:require [clojure.data :refer [diff Diff]]))
;; 直接使用即可
(diff 1 2)
(defrecord MyRecord [x]
Diff
(diff-similar [a b]
(= (:x a) (:x b))))
3.為命名空間起別名
(ns my-project.core
(:require [clojure.data :as data]))
;; 使用時需要指定成員所屬的命名空間的別名
(data/diff 1 2)
4.重命名注入的成員
(ns my-project.core
(:require [clojure.data :refer [diff] :rename {diff difference}]))
;; 使用時僅能使用別名
(difference 1 2)
;; (diff 1 2) 這里使用原名會報錯
5.引入同命名空間的marco
;; 引入helper.core下的所有macro
(ns my-project.core
(:require [helper.core :as h :include-macros true]))
(h/i-am-macro1)
(h/i-am-macro2)
(h/i-am-function)
;; 引入helper.core下指定的macro
(ns my-project.core
(:require [helper.core :as h :refer-macros [i-am-macro1]]))
(h/i-am-macro1)
;; 可以不用指定marco所屬的命名空間哦!
(i-am-macro1)
(h/i-am-function)
helper/core.cljs文件
(ns helper.core)
(defn i-am-function []
(println "i-am-function"))
helper/core.clj文件
(ns helper.core)
(defmacro i-am-macro1 []
'(println "i-am-macro1"))
(defmacro i-am-macro2 []
'(println "i-am-macro2"))
由于macro是在編譯期展開為列表,然后在運行時解析列表,而JS作為腳本語言根本就沒有所有編譯期,因此需要將macro寫在獨立的clj文件中,然后在cljs編譯為js時展開。所以當我們在同一個命名空間定義普通成員和macro時,只需命名兩個名稱一樣當擴展名不同的cljs和clj即可。
6.一次引入多個命名空間
(ns my-project.core
(:require [clojure.data :as data]
[cljs.test :refer [is]]
clojure.string))
通過:use
:use
其實相當于:require
加上:refer
那樣,一般建議用后者代替。
(ns my-project.core
(:use clojure.data :only [diff Diff]))
(diff 1 2)
(ns my-project.core
(:use clojure.data :only [diff] :rename {diff difference}))
(difference 1 2)
通過:require-macros
引入macro
其實通過:require
中引入macro已經間接接觸到:require-macros
了,因為它實際上會解析成:require-macros
來使用的!
1.為命名空間起別名
(ns my-project.core
(:require-macros helper.core :as h))
(h/i-am-macro1)
2.注入macro到當前命名空間
(ns my-project.core
(:require-macros helper.core :refer [i-am-macro1]))
(i-am-macro1)
3.注入macro到當前命名空間,并起別名
(ns my-project.core
(:require-macros helper.core :refer [i-am-macro1] :rename {i-am-macro1 m1}))
(m1)
通過:use-macros
引入macro
:use-macros
其實相當于:require-macros
加上:refer
那樣,一般建議用后者代替。
(ns my-project.core
(:use-macros helper.core :only [i-am-macro1]))
(i-am-macro1)
(ns my-project.core
(:use-macros helper.core :only [i-am-macro1] :rename {i-am-macro1 m1}))
(m1)
通過:import
引入Google Closure中的類型和枚舉類
注意:import
只能用于引入Google Closure中的類型,而其他類型、成員等等全部用:require
引入就好了。
(ns my-project.core
(:import goog.math.Long
[goog.math Vec2 Vec3]))
(Long. 4 6)
(Vec2. 1 2)
(Vec3. 1 2 3)
通過:refer-clojure
重置clojure內置的symbol
我們知道默認情況下會自動注入cljs.core
的成員到當前命名空間中,因此我們可以直接使用+
、-
等函數。如果此時我們自定義一個名為+
的函數,那么就會讓下次要使用加法函數時則需要寫成cljs.core/+
,這樣總感覺不太好。那么我們可以借助:refer-clojure
來重置這些內置symbol了。
(ns my-project.core
(:refer-clojure :rename {+ math_add}))
(defn + [& more]
(apply math_add more))
另外還可以直接丟棄(不用就不要注入夠環保的啊!)
(ns my-project.core
(:refer-clojure :exclude [+]))
(+) ;; 報錯了!
驚喜:命名空間clojure.*
將自動轉為cljs.*
cljs的好處就是可以直接使用與宿主環境無關的clj代碼,所以我們可以直接引入clojure.string
、clojure.data
等命名空間,但有時不免會記錯或新版本提供了更貼地氣(針對特定宿主優化過)的版本,那是不是就要改成cljs的版本呢?放心cljs編譯器會自動幫你搞定!
(ns testme.core (:require [clojure.test]))
;; 會自動轉換為
(ns testme.core (:require [cljs.test :as clojure.test]))
require
用在REPL中就好了
在REPL中我們會使用如require
、use
、require-macros
、import
等macro來引入命名空間。請緊記,這些確實僅僅用于REPL中而已。而且當我們修改源碼后,需要通過(require 命名空間 :reload)
來重置并重新加載這個命名空間,不帶:reload
的話新修改的功能將不會生效哦!
注意:require
后的命名空間需要以單引號為起始,從而避免將其從symbol解析為var然后取其值。如
(require 'clojure.data)
(require '[clojure.set :as s])
最佳實踐
根據clojure-style-guide描述優先級別如下:
:require :as
> :require :refer
:require
> :use
而聲明順序如下:
:refer-clojure
>:require
>:import
總結
現在我們可以安心開始書寫第一個自定義命名空間了,但是不是還是有點不安穩的感覺呢?是不是上面提到Special Form
、Symbol
、Var
等一頭霧水呢?下一篇(cljs/run-at (JSVM. :browser) "簡單類型可不簡單啊~")
尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/7096800.html ^_^肥仔John
文章列表