(cljs/run-at (JSVM. :browser) "命名空間就這麼簡單")

前言

 一個cljs文件定義一個命名空間,經過命名空間能夠有效組織代碼,這是構建大型系統必備的基礎設施。本篇咱們就深刻理解cljs中的命名空間吧!html

好習慣從"頭"開始

每一個cljs文件首行非註釋的內容一定以下git

(ns my-project.core)

而當前的cljs文件路徑爲${project_dir}/src/my_project/core.cljs,很明顯命名空間與源碼文件路徑是一一對應的,對應規則是-對應_.對應/咯~github

引入其餘命名空間

 要使用其餘命名空間下的成員,那麼必須先將其引入到當前命名空間才能夠。但注意的是,默認狀況下會自動引入cljs.core這個命名空間,並且會將其成員注入到當前命名空間中。所以(ns my-project.core)最後會編譯爲等價於如下語句app

;; 注意:cljs中並不支持:all這種引入,所以這面語句僅僅用於表達注入全部成員而已
(ns my-project.core
 (:require [cljs.core :all]))

因此咱們能夠直接調用reduce而不是cljs.core/reduce
 咱們沒可能只調用cljs.core的成員吧,那到底如何引入其餘命名空間呢?下面咱們一一道來!ide

經過: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.爲命名空間起別名ui

(ns my-project.core
 (:require [clojure.data :as data]))

;; 使用時須要指定成員所屬的命名空間的別名
(data/diff 1 2)

4.重命名注入的成員code

(ns my-project.core
 (:require [clojure.data :refer [diff] :rename {diff difference}]))

;; 使用時僅能使用別名
(difference 1 2)
;; (diff 1 2) 這裏使用原名會報錯

5.引入同命名空間的marcoorm

;; 引入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.stringclojure.data等命名空間,但有時難免會記錯或新版本提供了更貼地氣(針對特定宿主優化過)的版本,那是否是就要改爲cljs的版本呢?放心cljs編譯器會自動幫你搞定!

(ns testme.core (:require [clojure.test]))
;; 會自動轉換爲
(ns testme.core (:require [cljs.test :as clojure.test]))

require用在REPL中就行了

 在REPL中咱們會使用如requireuserequire-macrosimport等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 FormSymbolVar等一頭霧水呢?下一篇(cljs/run-at (JSVM. :browser) "簡單類型可不簡單啊~")
尊重原創,轉載請註明來自:http://www.cnblogs.com/fsjohn... ^_^肥仔John

相關文章
相關標籤/搜索