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

简介:

前言

 一个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.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/fsjohnhuang/p/7096800.html ^_^肥仔John

如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!

 
本文转自 ^_^肥仔John博客园博客,原文链接: http://www.cnblogs.com/fsjohnhuang/p/7096800.html,如需转载请自行联系原作者
相关文章
|
Android开发
web project导入eclipse ,针对run as里 没有 run on server 选项问题?
web project导入eclipse ,针对run as里 没有 run on server 选项问题?
548 0
web project导入eclipse ,针对run as里 没有 run on server 选项问题?
|
4月前
【Azure 应用服务】Web App Service 中的 应用程序配置(Application Setting) 怎么获取key vault中的值
【Azure 应用服务】Web App Service 中的 应用程序配置(Application Setting) 怎么获取key vault中的值
|
4月前
|
Shell PHP Windows
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
|
5月前
|
Web App开发 数据采集 JavaScript
我们来看一个基本的`pyppeteer`使用示例,包括`launch()`和`newPage()`方法。
我们来看一个基本的`pyppeteer`使用示例,包括`launch()`和`newPage()`方法。
|
JavaScript Shell
electron使用child_process打开外部应用
electron使用child_process打开外部应用
|
存储 XML 缓存
loadrunner 运行脚本-Run-time Settings-Browser Enmulation设置详解
loadrunner 运行脚本-Run-time Settings-Browser Enmulation设置详解
139 0
|
JavaScript Unix
web pack 在js区分是 run dev还是 run build
package.json 配置 cross-env NODE_ENV=production "build": "cross-env NODE_ENV=production webpack --progress -...
1147 0