开源中国

我们不支持 IE 10 及以下版本浏览器

It appears you’re using an unsupported browser

为了获得更好的浏览体验,我们强烈建议您使用较新版本的 Chrome、 Firefox、 Safari 等,或者升级到最新版本的IE浏览器。 如果您使用的是 IE 11 或以上版本,请关闭“兼容性视图”。
ClojureScript 简单介绍 - 技术翻译 - 开源中国社区

ClojureScript 简单介绍 【已翻译100%】

oschina 推荐于 2年前 (共 8 段, 翻译完成于 12-04) 评论 12
收藏  
20
推荐标签: ClojureScript 待读

这篇文章经过 Thomas Greco Jérémy Heleine 的同行评议。感谢所有 SitePoint 的评议者们,是你们使 SitePoint 的内容如此的棒!

从几个月前起,越来越多的开发者秉承着“抱紧 JavaScript 的大腿”的理念,然而,被编译为 JavaScript 的编程语言与日俱增,其中包含有 Dart、TypeScript、CoffeeScript、ClojureScript 等。

在这篇文章中我们会讨论一下 ClojureScript,一种目标为 JavaScript 的新 Clojure 编译器。我们会探讨一下使用 ClojureScript 的好处,以及怎么样快速的使用 npm 和“你们最爱的”Node.js 库搭建起它的环境。

david203812
 翻译得不错哦!

为什么使用ClojureScript?

在网上有很多的文章解释了 ClojureScript 的好处,总的来说有以下几点:

  • 简洁性: 从语法角度来说,ClojureScript 是一种基于 Lisp 的语言,这使得它的语法十分简洁。它的简洁性使得我们甚至能在本篇文章中完全地解释它。除此之外,ClojureScript 还提供了帮助我们更方便的异步编程的工具。

  • 安全性: 这意味着更少的漏洞!ClojureScript 和其他的函数编程语言都有很多的特性来帮助我们减少和减轻一些常见的漏洞。

  • 高性能: ClojureScript 使用了Google的Closure编译器(译者注:需翻墙),这使得ClojureScript能支持无用代码清除以及其他的一些特点。

  • 实时编程: ClojureScript 生态圈提供了很多的工具来“实时编程”。这意味着当代码一改变,在你的项目上就会马上反应出来。在这篇文章中我们会看下Figwheel使读者能更好的理解这个概念。

  • 代码复用性: ClojureScript 能在全局中运行,很多人称之“同态”。这代表你能在服务器端和客户端运行相同的代码,这在 Node.js 的生态系统中已经是个流行的模式了。除此之外,ClojureScript 还能使用 Node.js 和 Java 生态圈当中的库。

david203812
 翻译得不错哦!

安装 Clojure(Script) 工具链

在这篇文章中,我们将在 Mac OSX 环境中安装工具链。以防万一,在这里你能找到 ClojureScript wik i中在其他环境下的安装指南。首先我们将会需要一些系统依赖项,其中一个就是 OSX 下流行的包管理器,Homebrew

安装最新版本的 Java

ClojureScript 需要最新版本的 Java (在这篇文章完成的时候为 Java8)。如果在运行 lein 这个命令的时候你遇到了一个看起来像这样的错误:

Exception in thread "main" java.util.regex.PatternSyntaxException: 
    Unknown inline modifier near index 2 (?U)^[\p{Alpha}_$]^, compiling:(cljs/util.clj:158:33)

那么你就需要去安装最新的 Java 了。

首先,让我们在命令行界面中运行如下命令:

brew tap caskroom/cask
brew install brew-cask

如果终端出现了“already installed”这个错误,根据你终端中的引导将链接取消。一旦完成,再次安装。如下命令可以完成这个动作:

brew unlink brew-cask
brew install brew-cask

此时,执行我们需要的最后一个命令:

brew cask install java

安装Leiningen

Leiningen 是一个 Clojure 项目的构建工具,我们将用它来执行 ClojureScript 代码并安装依赖项。这一步假设你已经安装了 Homebrew

brew install leiningen

如果这步失败了,也许你需要手动安装

david203812
 翻译得不错哦!

使用Repl

既然现在我们已经安装了 Leningen,我们就可以开始熟悉 ClojureScript 的语法了。

执行

lein rep

命令,你应该会得到类似于这样的输出:

$ lein repl
nREPL server started on port 58371 on host 127.0.0.1 - nrepl://127.0.0.1:58371
REPL-y 0.3.7, nREPL 0.2.10
Clojure 1.7.0
Java HotSpot(TM) 64-Bit Server VM 1.6.0_65-b14-466.1-11M4716
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=>

我们现在已经在 ClojureScript Repl 中了,这让我们能快速执行 ClojureScript 并且查看结果。如果要离开 repl 你可以按下 Control+D。

执行这步之后,我们现在已经准备好在 ClojureScript 的语法海洋遨游并做点好玩的事啦!

ClojureScript语法

ClojureScript 是一种函数语言,这代表它使用函数以及有限的额外语言结构。在接下来的模块中我将会讲述这种语言的一些特性。

原生类型

ClojureScript 支持下列的原生类型:

  • 数字

    user=> 1.23
        1.23
  • 字符串

    user=> "foo"
        "foo"
  • 矢量(数组)

    user=> [:bar 3.14 "hello"]
        [:bar 3.14 "hello"]
  • 字典(混合数组)

    user=> {:msg "hello" :pi 3.14 :primes [2 3 5 7 11 13]}
        {:msg "hello", :pi 3.14, :primes [2 3 5 7 11 13]}
  • 关键字(用于访问字典)

    user=> :foo
        :foo
  • 集合(唯一数组)

    user=> #{:bar 3.14 "hello"}
        #{"hello" 3.14 :bar}
david203812
 翻译得不错哦!

全都是函数

函数是 ClojureScript 的基础组件。你甚至是通过内置的 defn 函数来定义自己的函数。

下面是一个函数定义的例子。我们要定义一个叫 myfunction 的函数。这个函数有一个参数,叫 argument1,然后简单的返回这个参数。这个函数没啥用,但是可以很好的为我们示范语法。

user=> (defn myfunction [argument1] argment1)

估计你会觉得这个语法有些怪异,下面是 Javascript 的等价代码:

function myfunction(argument1){
    return argument1;}

通过圆括号把函数名和参数包含起来的方式来调用函数。

user=> (myfunction "hello world")
"hello world"

在非函数式编程语言里,可以使用特殊的“操作符”或者关键字。例如 Javascript 中,通常可以用到 + - == if。但是在 ClojureScript 和其他基于 Lisp 的语言里,没有特殊的操作符,只有常规的函数。

If 表达式是一个函数:

user=> (if true "do true stuff here" "do false stuff here")
"do true stuff here"

数学表达式也是函数,如下所示:

user=> (+ 2 3)
5
user=> (* 2 3)
6

关于更多的 Javascript 与 ClojureScript 的等价代码的示例,可以参考这个网站

zicode
 翻译得不错哦!

创建一个 Node.js-Clojure 项目

创建一个 ClojureScript 项目是十分方便的,Leningen 提供了项目模版来激起你的兴趣并使你能迅速地掌握怎么运行一个样板化项目。

模版是一种让我们实验并熟悉一些功用和设置的很好的资源。Clojars.org上有许多的模版,同时,你也可以在网上找到其他一些。在我们的项目当中,我们将使用 Nodejs Figwheel 项目模版

首先,让我们在命令行界面中运行如下命令来开始:

$ lein new figwheel-node hello-world

这会在 ./hello-world 目录下创建一个新的 ClojureScript 项目。这篇文章的余下部分将假设项目名称是 hello-world,当然了,如果你想的话你可以使用其他名字,但我建议你保留这个项目名,如此一来你可以跟着文章走并且不需要害怕有什么东西会出错。

接下来,进到我们创建的目录当中并安装 npm 依赖项:

$ cd hello-world
$ npm install
david203812
 翻译得不错哦!

关注点

项目文件夹中包含一些文件,在这个段落中我将突出强调一些关于它们的重要概念:

  • package.json:这对于Node.js用户来说应该很熟悉了,我们的npm依赖项将会添加在这个文件中。

  • project.clj:这个文件是ClojureScript项目的配置文件,可以说是ClojureScript版本的package.json。我们能在这里配置Clojure依赖项以及编译目标。这个文件同样包含如标题和叙述的项目细节

  • figwheel.js: 这个文件只在Figwheel项目中存在,是我们项目的启动文件。它将Figwheel指向我们的源代码让它来监听代码更新。我们将使用node figwheel.js命令来运行它。

  • ./src/hello-world/core.cljs:这是我们的入口资源文件,也就是我们启动项目的地方。把它想象成类似于Node.js项目中的一个index.js文件吧。

core.cljs文件包含以下的内容,我添加了注释使你能明白发生了什么:

;; 这里声明了一个当前文件的命名空间以及必要依赖项
(ns hello-world.core
  (:require [cljs.nodejs :as nodejs]))

;; 这里升级了默认的println方法使它能写到Node.js的stdout当中
(nodejs/enable-util-print!)

;; 模块的主函数
;; 它将会打印并输出"Hello World!"到stdout中
(defn -main []
  (println "Hello world!"))

;; *main-cli-fn* 是一个用于为node应用设定入口*point的“半魔法”变量
(set! *main-cli-fn* -main)

运行项目

若要运行当前项目,打开终端窗口并移动到我们的hello-world项目文件夹中,然后执行以下命令:

lein figwheel

这将启动Figwheel监听需要构建的更新,将这个窗口放着让他运行并开启一个新的终端。在这个终端中再次移动到项目文件夹中并执行以下命令:

node figwheel.js

你应该会看到如下的“Hello world”输出:

$ node figwheel.js
Hello world!
Figwheel: trying to open cljs reload socket
Figwheel: socket connection established
david203812
 翻译得不错哦!

使用 Express.js 作为服务器

既然现在我们已经搭建好了 ClojureScript 项目的基础,我们就可以开始在新的终端中使用一些我们熟悉的库了。在 hello-world 文件夹中执行以下命令:

npm install --save express

然后我们需要将./src/hello-world/core.cljs更新为:

(ns hello-world.core
  (:require [cljs.nodejs :as nodejs]
            [clojure.string :as string]))

(nodejs/enable-util-print!)

(defonce express (nodejs/require "express"))
(defonce http (nodejs/require "http"))
(defonce server-port 3000)

(def app (express))

(. app (get "/hello"
      (fn [req res] (. res (send "Hello world")))))

(def -main
  (fn []
    (doto (.createServer http #(app %1 %2))
      (.listen server-port))))
      (.listen server))))
    (println (string/join " " ["Server running on" server-port]) )
    

(set! *main-cli-fn* -main)

现在当你在项目中执行 node figwheel.js 时,你应该会看到 running on 3000 的输出。如果你访问http://localhost:3000/hello,你应该会看到 express route 的输出“Hello world.”

结语

在本篇文章中我们讨论了如何建立一个新的 ClojureScript 项目并在其中安装常用的 Node 依赖项。这为我们将 ClojureScript 作为一种语言来理解打下了很好的基础。同时我将这篇文章用到的源代码放上了 Github,它已经有些超出了本文的范围,演示了如何整合 React 服务器端渲染。

david203812
 翻译得不错哦!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
评论(12)
Ctrl/CMD+Enter

编程语言本身具有语言的社会性,因此自由开放是最符合语言自然规律的
Clojure的storm也失守了,喜欢list的童鞋,有点担忧了~
clj 是 Lisp 翻身最近一次失败. 希望下次有基于 LLVM 并且有高阶类型系统的 Lisp 出现. .
Map 直接翻译成地图`~~我也是醉了`~`
越搞越复杂

引用来自“dodojohn”的评论

Map 直接翻译成地图`~~我也是醉了`~`
赶紧ctrl+f搜索一下,果然有地图。哈哈。
随手一翻就看到一个“地图”...

引用来自“二的基本算合格”的评论

Clojure的storm也失守了,喜欢list的童鞋,有点担忧了~
这个也叫失手?

引用来自“东东-”的评论

clj 是 Lisp 翻身最近一次失败. 希望下次有基于 LLVM 并且有高阶类型系统的 Lisp 出现. .
何谓失败?

引用来自“二的基本算合格”的评论

Clojure的storm也失守了,喜欢list的童鞋,有点担忧了~

引用来自“moose123”的评论

这个也叫失手?
阿里的JStorm已经贡献给Apache,关于JStorm与Storm的比较有文章,JStorm是Strom的Java版本,有消息传下个版本的Strom将回归Java以JStorm为主干,也就是说Storm将极有可能用Java代替Clojure。 不管对失守如何理解,至少这个消息是让Cljer不愉快。 我也会在闲暇之余尝试Clojure、Haskell也希望能推广它们,但是缺少过硬的实际应用真的是硬伤。

引用来自“二的基本算合格”的评论

Clojure的storm也失守了,喜欢list的童鞋,有点担忧了~

引用来自“moose123”的评论

这个也叫失手?

引用来自“二的基本算合格”的评论

阿里的JStorm已经贡献给Apache,关于JStorm与Storm的比较有文章,JStorm是Strom的Java版本,有消息传下个版本的Strom将回归Java以JStorm为主干,也就是说Storm将极有可能用Java代替Clojure。 不管对失守如何理解,至少这个消息是让Cljer不愉快。 我也会在闲暇之余尝试Clojure、Haskell也希望能推广它们,但是缺少过硬的实际应用真的是硬伤。
用java代替clj作为storm下个版本应该几率很大,这到无所谓,毕竟clj小众,不是人人都爱折腾,招懂clj人更是少,所以用java代替很正常,至少现在版本storm已经很成功了,所以无需要证明什么了,clojure谁用谁知道的爽。
最后的 core.cljs 代码有误,多了几个尾括号,正确的代码如下:


(ns hello-world.core
(:require [cljs.nodejs :as nodejs]
[clojure.string :as string]))

(nodejs/enable-util-print!)

(defonce express (nodejs/require "express"))
(defonce http (nodejs/require "http"))
(defonce server-port 3000)

(def app (express))

(. app (get "/hello"
(fn [req res] (. res (send "Hello world")))))

(def -main
(fn []
(doto (.createServer http #(app %1 %2))
(.listen server-port)
(.listen server))))
(println (string/join " " ["Server running on" server-port]) )


(set! *main-cli-fn* -main)
顶部