前言

?一個(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、userequire-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