(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
相关文章推荐
- (cljs/run-at (JSVM. :browser) "简单类型可不简单啊~")
- (cljs/run-at (JSVM. :browser) "搭建刚好可用的开发环境!")
- (cljs/run-at (->JSVM :browser) "语言基础")
- (cljs/run-at (JSVM. :all) "一起实现柯里化")
- (cljs/run-at (JSVM. :all) "一次说白DataType、Record和Protocol")
- (cljs/run-at (JSVM. :all) "细说函数")
- (cljs/run-at (JSVM. :all) "Metadata就这样哦")
- 转载:ASP.NET中命名空间的简单介绍
- 关于反射Assembly.Load("程序集").CreateInstance("命名空间.类")
- 关于反射Assembly.Load("程序集").CreateInstance("命名空间.类")
- ASP.NET中的命名空间的简单介绍
- "命名空间"system.web"中不存在类型或命名空间名称security"错误解决方法
- 命名空间"system.web"中不存在类型或命名空间名称security
- 分析器错误信息: 未能加载类型"命名空间.类"... ---小结
- 分析器错误信息: 未能加载类型"命名空间.类"... ---小结
- 命名空间"System.web”中不存在类型或命名空间名称"UI”(是缺少程序集引用)
- C# 引用其他命名空间的公共变量时的警告:由于"***"是引用封送类的字段,访问上面的成员可能导致运行时异常
- <table runat="server"> 元素要在增加行TR的TD中内嵌
- 命名空间"Microsoft中不存在类型或命名空间名称Office"解决办法
- 未能加载类型"命名空间.类"分析