博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(cljs/run-at (JSVM. :browser) "命名空间就这么简单")
阅读量:6210 次
发布时间:2019-06-21

本文共 4222 字,大约阅读时间需要 14 分钟。

前言

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

最佳实践

根据描述优先级别如下:

:require :as > :require :refer
:require > :use
而声明顺序如下:
:refer-clojure>:require>:import

总结

 现在我们可以安心开始书写第一个自定义命名空间了,但是不是还是有点不安稳的感觉呢?是不是上面提到Special FormSymbolVar等一头雾水呢?下一篇(cljs/run-at (JSVM. :browser) "简单类型可不简单啊~")

尊重原创,转载请注明来自: ^_^肥仔John

你可能感兴趣的文章
人社部:加强人才队伍建设 深化事业单位人事制度改革
查看>>
上海豫园新春民俗艺术灯会亮灯 “财神猪”受热捧
查看>>
上海率先成立政府主导的“筛查诊治中心”
查看>>
EOS单日暴涨41%,市值超过莱特币,位居虚拟货币第5
查看>>
中国大型舞剧《大梦敦煌》惊艳狮城
查看>>
B站弹幕机,看看月薪20K程序员怎么在10分钟用原生javascript写出来!
查看>>
聊聊微软刚发布的区块链去中心化身份识别系统DID
查看>>
从 Auto Layout 的布局算法谈性能
查看>>
学了这么久,vue和微信小程序到底有什么样的区别?
查看>>
数据运营实战(四)——男性女性,到底谁的购买率高?
查看>>
比特币的区块结构解析
查看>>
非功能性需求,不要成为项目的坑
查看>>
深入Weex系列(一)之Weex入门准备
查看>>
Swift4 0版 H5页面实现长按保存图片
查看>>
Vue教程00:MVC、MVP、MVVM模式的区别,服务端渲染与客户端渲染的区别
查看>>
系统优化总结——系统层面
查看>>
故事 | 一场发生在AI实验室的离奇血案
查看>>
支付宝防并发方案之"一锁二判三更新"
查看>>
Vue2.x源码解析系列七:深入Compiler理解render函数的生成过程
查看>>
使用 D8 分析 javascript 如何被 V8 引擎优化的
查看>>