(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

相关文章
相关标签/搜索