zoukankan      html  css  js  c++  java
  • (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

  • 相关阅读:
    Enterprise Library3.1 使用数据访问模块时,调用Microsoft.Practices.EnterpriseLibrary.Data报出源文件与当前应用程序不一致和创建dataconfiguration的配置节处理程序出错
    net精华:C#中对注册表的操作
    [翻译]使用Enterprise Library 3.0的日志程序块
    分布式应用程序概述
    调整Oracle数据库print_bill表字段BillMKID的顺序,并判断表print_bill是否存在及字段billMKID是否存在
    Win32下注册COM组件后对注册表产生的变动
    vc 字符串与指针
    SQL Server不允许进行远程连接的解决办法
    vc上字符串,CString ,string,char数组&char指针
    如何用Visual C#来创建、修改注册信息
  • 原文地址:https://www.cnblogs.com/fsjohnhuang/p/7096800.html
Copyright © 2011-2022 走看看