前言
?一個(gè)cljs文件定義一個(gè)命名空間,通過命名空間可以有效組織代碼,這是構(gòu)建大型系統(tǒng)必備的基礎(chǔ)設(shè)施。本篇我們就深入理解cljs中的命名空間吧!
好習(xí)慣從"頭"開始
每個(gè)cljs文件首行非注釋的內(nèi)容必定如下
(ns my-project.core)
而當(dāng)前的cljs文件路徑為${project_dir}/src/my_project/core.cljs
,很明顯命名空間與源碼文件路徑是一一對(duì)應(yīng)的,對(duì)應(yīng)規(guī)則是-
對(duì)應(yīng)_
,.
對(duì)應(yīng)/
咯~
引入其他命名空間
?要使用其他命名空間下的成員,那么必須先將其引入到當(dāng)前命名空間才可以。但注意的是,默認(rèn)情況下會(huì)自動(dòng)引入cljs.core
這個(gè)命名空間,而且會(huì)將其成員注入到當(dāng)前命名空間中。因此(ns my-project.core)
最后會(huì)編譯為等價(jià)于以下語句
;; 注意:cljs中并不支持:all這種引入,因此這面語句僅僅用于表達(dá)注入所有成員而已(ns my-project.core (:require [cljs.core :all]))
所以我們可以直接調(diào)用reduce
而不是cljs.core/reduce
。
?我們沒可能只調(diào)用cljs.core
的成員吧,那到底如何引入其他命名空間呢?下面我們一一道來!
通過:require
1.直接引入
(ns my-project.core (:require clojure.data));; 使用時(shí)需要指定成員所屬的命名空間(clojure.data/diff 1 2)
2.注入成員到當(dāng)前命名空間
; 將clojure.data/diff和clojure.data/Diff兩個(gè)成員注入到當(dāng)前命名空間(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]));; 使用時(shí)需要指定成員所屬的命名空間的別名(data/diff 1 2)
4.重命名注入的成員
(ns my-project.core (:require [clojure.data :refer [diff] :rename {diff difference}]));; 使用時(shí)僅能使用別名(difference 1 2);; (diff 1 2) 這里使用原名會(huì)報(bào)錯(cuò)
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是在編譯期展開為列表,然后在運(yùn)行時(shí)解析列表,而JS作為腳本語言根本就沒有所有編譯期,因此需要將macro寫在獨(dú)立的clj文件中,然后在cljs編譯為js時(shí)展開。所以當(dāng)我們?cè)谕粋€(gè)命名空間定義普通成員和macro時(shí),只需命名兩個(gè)名稱一樣當(dāng)擴(kuò)展名不同的cljs和clj即可。
6.一次引入多個(gè)命名空間
(ns my-project.core (:require [clojure.data :as data] [cljs.test :refer [is]] clojure.string))
通過:use
?:use
其實(shí)相當(dāng)于: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
其實(shí)通過:require
中引入macro已經(jīng)間接接觸到:require-macros
了,因?yàn)樗鼘?shí)際上會(huì)解析成:require-macros
來使用的!
1.為命名空間起別名
(ns my-project.core (:require-macros helper.core :as h)) (h/i-am-macro1)
2.注入macro到當(dāng)前命名空間
(ns my-project.core (:require-macros helper.core :refer [i-am-macro1])) (i-am-macro1)
3.注入macro到當(dāng)前命名空間,并起別名
(ns my-project.core (:require-macros helper.core :refer [i-am-macro1] :rename {i-am-macro1 m1})) (m1)
通過:use-macros
引入macro
?:use-macros
其實(shí)相當(dāng)于: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內(nèi)置的symbol
?我們知道默認(rèn)情況下會(huì)自動(dòng)注入cljs.core
的成員到當(dāng)前命名空間中,因此我們可以直接使用+
、-
等函數(shù)。如果此時(shí)我們自定義一個(gè)名為+
的函數(shù),那么就會(huì)讓下次要使用加法函數(shù)時(shí)則需要寫成cljs.core/+
,這樣總感覺不太好。那么我們可以借助:refer-clojure
來重置這些內(nèi)置symbol了。
(ns my-project.core (:refer-clojure :rename {+ math_add})) (defn + [& more] (apply math_add more))
?另外還可以直接丟棄(不用就不要注入夠環(huán)保的??!)
(ns my-project.core (:refer-clojure :exclude [+])) (+) ;; 報(bào)錯(cuò)了!
驚喜:命名空間clojure.*
將自動(dòng)轉(zhuǎn)為cljs.*
?cljs的好處就是可以直接使用與宿主環(huán)境無關(guān)的clj代碼,所以我們可以直接引入clojure.string
、clojure.data
等命名空間,但有時(shí)不免會(huì)記錯(cuò)或新版本提供了更貼地氣(針對(duì)特定宿主優(yōu)化過)的版本,那是不是就要改成cljs的版本呢?放心cljs編譯器會(huì)自動(dòng)幫你搞定!
(ns testme.core (:require [clojure.test]));; 會(huì)自動(dòng)轉(zhuǎn)換為(ns testme.core (:require [cljs.test :as clojure.test]))
require
用在REPL中就好了
?在REPL中我們會(huì)使用如require
、use
、require-macros
、import
等macro來引入命名空間。請(qǐng)緊記,這些確實(shí)僅僅用于REPL中而已。而且當(dāng)我們修改源碼后,需要通過(require 命名空間 :reload)
來重置并重新加載這個(gè)命名空間,不帶:reload
的話新修改的功能將不會(huì)生效哦!
?注意:require
后的命名空間需要以單引號(hào)為起始,從而避免將其從symbol解析為var然后取其值。如
(require 'clojure.data) (require '[clojure.set :as s])
最佳實(shí)踐
根據(jù)clojure-style-guide描述優(yōu)先級(jí)別如下::require :as
> :require :refer
:require
> :use
而聲明順序如下::refer-clojure
>:require
>:import
總結(jié)
?現(xiàn)在我們可以安心開始書寫第一個(gè)自定義命名空間了,但是不是還是有點(diǎn)不安穩(wěn)的感覺呢?是不是上面提到Special Form
、Symbol
、Var
等一頭霧水呢?下一篇(cljs/run-at (JSVM. :browser) "簡(jiǎn)單類型可不簡(jiǎn)單啊~")
尊重原創(chuàng),轉(zhuǎn)載請(qǐng)注明來自:http://www.cnblogs.com/fsjohnhuang/p/7096800.html ^_^肥仔John
如果您覺得本文的內(nèi)容有趣就掃一下吧!捐贈(zèng)互勉!
http://www.cnblogs.com/fsjohnhuang/p/7096800.html