您的位置:首页 > Web前端 > JavaScript

(cljs/run-at (JSVM. :browser) "命名空间就这么简单")

2017-06-30 08:46 423 查看

前言

 一个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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: