前言

 一个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

(cljs/run-at (JSVM. :browser) "命名空间就这么简单")的更多相关文章

  1. (cljs/run-at (JSVM. :browser) "搭建刚好可用的开发环境!")

    前言  书接上一回,在了解cljs基本语法后并在clojurescript.net的奇特错误提示后,我们必须痛定思痛地搭建一个本地的开发环境,以便后续深入地学习cljs. 现有的构建工具  由于浏览器 ...

  2. (cljs/run-at (->JSVM :browser) "语言基础")

    前言  两年多前知道cljs的存在时十分兴奋,但因为工作中根本用不上,国内也没有专门的职位于是搁置了对其的探索.而近一两年来又刮起了函数式编程的风潮,恰逢有幸主理新项目的前端架构,于是引入Ramda. ...

  3. (cljs/run-at (JSVM. :browser) "简单类型可不简单啊~")

    前言  每逢学习一个新的语言时总要先了解这门语言支持的数据类型,因为数据类型决定这门语言所针对的问题域,像Bash那样内置只支持字符串的脚步明显就是用于文本处理啦.而数据类型又分为标量类型(Scala ...

  4. [browser location和history] 简单实现了个路由[转载]

    今天看了1下前面写的,好像缺乏交流性,周末再来弄吧 -0- 今天看了browser 的 location 和 history location属性 // //location.hash 性是一个可读可 ...

  5. Previous operation has not finished; run 'cleanup' if it was interrupted最简单有效的解决方法

    今天提交代码报错,看了看提示的错误,百度了一下,发现操作都比较繁琐,所以自己重新给一个最简单有效的. 有的要下载sqlite3.exe,借助它清空本地.svn\wc.db数据库文件里面的operati ...

  6. 微软官方网站线上兼容测试平台-Browser screenshots

    前端开发时最不想做的就是在不同浏览器.平台和分辨率测试网页显示效果,通常这会浮现许多问题,尤其浏览器版本就可能让显示成效完全不同,也只好尽力维持让每一种设备都能正常浏览网页.修改到完全没有问题必须投入 ...

  7. TypeScript学习笔记(七) - 命名空间

    本篇将介绍TypeScript的命名空间,并简单说明一下与模块的区别. 在之前的例子里,有如下一段代码,通过修改这段代码来演示命名空间的用法. interface Animal { name: str ...

  8. XAML的命名空间

    原文:XAML的命名空间 一个最简单的XAML例子   <Window x:Class="WpfApplication1.MainWindow" xmlns="ht ...

  9. Go routine 编排框架:oklog/run 包

    目录 Go routine 编排框架:oklog/run 包 问题引入 oklog/run 包介绍 使用例子 参考资料 Go routine 编排框架:oklog/run 包 问题引入 oklog/r ...

随机推荐

  1. 介绍几个python的音频处理库

    一.eyeD3 直接在google上搜索python mp3 process ,推荐比较多的就是这个第三方库了.先来看看官方介绍吧. About eyeD3 is a Python tool for ...

  2. 如何使用.bas文件

    1. 确保你安装的是word 2010,打开word文档,,按ALT + F11打开VBE编辑器. 2.点击Normal,右键,在弹出的对话框中选择导入文件. 2. 选择需要使用的脚本的位置,然后点击 ...

  3. 【面向对象设计原则】之里氏替换原则(LSP)

    里氏代换原则由2008年图灵奖得主.美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing 教授于1994年提出,所以使用的是这位女博士的性命名的一个 ...

  4. SmartCoder每日站立会议 01

    1.站立会议内容 确定今天团队成员各自的任务,并讨论今后各自的学习方向. 站立会议照片: 2.任务看板: 3.燃尽图:

  5. Asp.Net Core MVC项目实现多语言(Globalization/Localization)

    正好最近手上在给一个Razor MVC项目实现一个多语言功能,叫Globalization也好,Localization也好,whatever.最终要实现的效果呢,就是一键切换全站语言,并且开发的时候 ...

  6. Windows平台下python2和3的兼容问题解决

    很多朋友都安装了python2和3,因为用些库例如scapy,不是scrapy,python3下面都是错,那么怎么让python2和3共存呢. 像一般的程序员,达到如下效果 Windows平台下的兼容 ...

  7. SpringMVC——数据校验

    数据校验在web应用里是非常重要的功能,尤其是在表单输入中.在这里采用Hibernate-Validator进行校验,该方法实现了JSR-303验证框架支持注解风格的验证. 一.导入jar包 若要实现 ...

  8. C# 类型基础(中)

    前一篇文章中我们讲到了值类型和引用类型的一些区别,那这篇我们将深入的分析一下到底有什么不一样 先总结一下两者的差别: 黄金法则: 1.引用类型总是被分配到托管堆上. 2.值类型总是分配到它声明的地方: ...

  9. Python教程(2.6)——list和tuple简介

    Python中内置的类型有list和tuple. List list类似于C/C++的数组,可以存储多个数字.例如你可能会需要存储一个班里所有人的名字.这时就可以用到list.list中存储的数据叫做 ...

  10. 【JAVAEE学习笔记】hibernate01:简介、搭建、配置文件详解、API详解和CRM练习:保存客户

    今日学习:hibernate是什么 一.hibernate是什么 框架是什么: 1.框架是用来提高开发效率的 2.封装了好了一些功能.我们需要使用这些功能时,调用即可.不需要再手动实现. 3.所以框架 ...