clojure代码迁移到clojurescript过程中踩过的坑

 

    clojure是运行在java虚拟机上的函数式语言,而clojurescript是可以编译成javascript的函数式语言,两者之间的语法基本上是一致的。所以如果你写了一份clojure代码,既可以编译成java的jar包,被java项目代码调用,也可以将其转换为clojurescript代码,然后编译成js文件,被JavaScript项目调用,很方便。但是我本人从来没有接触过clojurescript代码,在由clojure代码转换成clojurescript代码的过程中,拆了许多坑,借此记录一下自己遇到的问题,由于是初学者,可能总结过程会有好多错误,请大家指教:

(1)使用use导入其他命名空间:

  在clojure中,我们可以直接用use导入命名空间的所有函数:

   而在clojurescript中,必须使用use/:only导入要使用的具体那些函数:


    当然在clojure中也可以使用:only这样导入,但是由于clojurescript限制了使用全部导入,有时候某个库里使用的函数比较多,必须一个一个加进去,这样是着实麻烦的事情。

(2)有些clojure中的函数,clojurescript是不能使用的:

   由于两者寄生的宿主不一样,所以clojure中所有依赖java实现的函数都不能在clojurescript中使用,同样clojurescript中所有依赖JavaScript实现的函数在clojure中也不能使用:

   1)如果你的代码中,有些函数直接调用了java的库,那么必须重新选择其他库或者自己实现;

   2)如果你的代码中,有些函数虽然不是java库中的函数,但是这些函数依然会在clojurescript中不存在,所以也必须重新实现,比如我在迁移过程中,遇到一个函数ref函数,它可以对一个对象实现引用,引用的好处就是可以修改对象的值,这在一些函数递归调用时实现参数传递时特别有用,但在clojurescript却没有。

(3)clojurescript中调用js函数:

   虽然clojurescript可以直接调用js库中的函数,但是调用的时候必须将该函数的参数转换为js可以识别的内容,可以用(clj->js)实现转换,所以在给js代码传递参数之前,一定要对参数进行转换!!!!!!

   样由于js函数返回的对象的格式js,所以如果想在clojurescript中使用js函数的返回结果,也必须用(js->clj)函数实现转换。

(4)再谈js->clj函数:

   clojurescript的map中如果key是一些带“:”关键字,在对其解构方面特别方便,但是大部分情况下,js给我们的map是没有关键字的,而是一个字符串,因此我们需要使用如下操作,如果遇到map将其中的key转换为关键字:


    如上图所示,将js->clj函数中的:keywordize-keys设置为true,便可以实现关键字转换。

    但是比较坑的一点是:js->clj函数转换过程中,没有对类型处理好,这样转换过来的map,虽然我们可以对其类型用map?函数实现正确判断,如下:


    但是当我们调用fmap时:



   fmap是定义了一个多重函数,根据s的类型进行不同函数的选择处理,这个时候由于传递进来的是map,需要识别clojure.long.IPersistentMap类型实现对map中的value的更改,但是这个时候用type函数判断js转换过来的map,竟然不是clojure.long.IPersistentMap类型,就是感觉好坑啊,转换过程中坑!!!!!!!感觉是clojurescrip设计的bug。

(5)提供外部接口:

   当我们使用lein cljsbuild once命令将clojurescript代码转换js文件时,这时候其实我们根本无法调用其中的函数,必须对js文件对外调用的接口用^:export关键字进行声明,才能实现外部的调用:


   如上图所示,process函数便可以被外部文件调用,凡是被外部接口调用函数,切记一定要将返回结果用(clj->js)函数转换为js可以识别的内容,如下,我们在另外一个js文件中调用该函数:


(6)clojurescript编译js代码:

   clojurescript工程配置如下:


    其中cljsbuild是我们在编译成js代码过程中,需要注意的地方:

   《1》source-paths:clojurescript源代码所在文件,编译时会将该文件下以及其子目录下的所有cljs文件  全部进行编译;

   《2》output-to:是最后编译生成的js代码放在那个文件中,所有cljs的文件代码最后只编译成一个js文件;

   《3》optimizations:该选项是为了控制Google Closure Compiler编译模式的:


      1)其中none是用于开发和调试时用的,可以将cljs文件编译成多个js文件;

      2)其中advanced可以将代码实现压缩,未使用的代码不进行编译,并会更名较短的名字,这个时候函数的  名字是随机的,只有使用^:export导出的函数才会正确匹配;

      3)其中whitespace是与:pretty-print配置一起使用的,编译出来的代码会比advanced长,但是该代码便   于阅读,这样的代码可以在浏览器进行调试观察

      4)其中simple还不是特别了解具体如何实现,待以后再补                      

   以上配置好以后,便可以将clojurescript编译成js代码,我们只需要在控制台使用lein cljsbuild once命令:

   

(7)编译成js代码的混淆:

   将clojurescript编译成js代码,会进行代码混淆,编译后的代码很难阅读,而且代码量巨大,它把所有用到的clojure的库都编译进去了,所以调试起来特别不容易,在调试过程中注意的是

  《1》编译时,会把clojurescript中使用到的“-”都编译成"_",在调用时一定要注意:

  《2》clojurescript中一些谓词函数(末尾带有问号的函数),会在末尾加上_QMARK_;

  《3》在调用外部接口函数时,一定记得在前面加上该函数所在命名空间;

  《4》以上内容都是针对提供外部接口的函数,其它函数简直没法看;

(8)JS中的异步编程问题:

    由于js中是异步编程的,也就是有好多好多promise(我是第一次接触js程序代码),我们如果我们编写的代码是同步,那么必须改成异步操作,这个真是一个大坑,只要有一处是异步的,往后的代码都必须返回promise,相信会js代码的人对promise很熟悉,我第一次接触就是感觉别扭:


   如上图所示,以前好好的clojure代码,当改成异步时,就是一大堆的.then函数操作,而且如果有多个promise进行操作时,还得进行一大堆嵌套,感觉好烦,以前好好的代码,就成这样了,由于.then函数是js的函数,所以其参数的回调函数必须将其用clj->js函数转换为js类型,才能在then函数中使用,异步接口调试过程中,真的是大坑,一步一步调试了好长时间。。。。

(9)最大的坑:

   最大的坑就是编译好的js文件,我们无法进行单步调试,而且代码难以读懂,简直就是坑,只要有一步错了,就得一行一行的往下编译,一点一点的调试,真的很累!更恐怖的是,每编译一次代码需要半分钟的时间,浪费时间啊浪费时间!!!!!!!!!!!!


 

猜你喜欢

转载自blog.csdn.net/zdplife/article/details/51581722
今日推荐