(ns my.namespace
(:require-macros [my.macros :as my]))
以下是對 https://clojure.dev.org.tw/about 和 https://clojure.dev.org.tw/reference 左側導覽面板上的章節進行逐節回顧,列舉 ClojureScript 的不同之處,以及在某些情況下的相似之處。
ClojureScript 的基本原理與 Clojure 大致相同,只是將 JavaScript 作為平台,並額外強調 JS 的普及性,因為它顯然不是一個那麼豐富的平台。
關於 ClojureScript 的基本原理的更深入討論可以在本網站的其他地方找到。
與 Clojure 一樣,ClojureScript 支援 REPL 驅動的開發,為各種 JavaScript 環境提供易於啟動的 REPL。有關詳細資訊,請參閱快速入門。
此外,ClojureScript 的自我託管能力支援將動態特性擴展到純 JavaScript 環境,可以在其中建立第三方 REPL 和其他動態設施。
Clojure 的數值、狀態、身分和時間模型即使在單執行緒環境中也很有價值。
Atoms 的運作方式與 Clojure 中相同
沒有 Refs 或 STM
binding
的使用者體驗與 Clojure 中類似
Vars
未在運行時重新化
通過分析器存取 Clojure 資料結構可以避免許多開發時重新化的使用
def
會產生普通的 JS 變數
目前尚未實作 Agents
ClojureScript 託管於 JavaScript VM
可選擇使用 Google 的 Closure 編譯器進行優化
它旨在利用 Google 的 Closure 函式庫,並參與其依賴/要求/提供機制
請參閱快速入門
數字
ClojureScript 目前僅支援對應到 JavaScript 基本型別的整數和浮點數文字
目前不支援 Ratio、BigDecimal 和 BigInteger 文字
數字的相等性運作方式類似 JavaScript,而非 Clojure:(= 0.0 0) ⇒ true
字元
ClojureScript 沒有字元文字。相反地,字元與 JavaScript 中的字元相同(即,單字元字串)
列表、向量、對應和集合文字與 Clojure 中相同
巨集字元
由於 ClojureScript 中沒有字元型別,因此 \
會產生單字元字串。
read
read
和 read-string
函數位於 cljs.reader
命名空間中
標記文字
與Clojure 標記文字相同,不同之處在於,在編譯階段使用的讀取器函數類似於 Clojurescript 巨集,它們應該返回 Clojurescript 程式碼形式(或文字,如字串或數字)。
Clojure 編譯器不會自動要求 data_readers.clj/c 中引用的讀取器函數,但 Clojurescript 編譯器會。
有關詳細資訊,請參閱讀取器
有關 ClojureScript REPL 的使用方式,請參閱快速入門。
標準 ClojureScript REPL 支援 Clojure main 模式。
ClojureScript 具有與 Clojure 相同的求值規則
load
存在,但僅作為 REPL 特殊函數
load-file
存在,但僅作為 REPL 特殊函數
雖然 Clojure 會執行局部變數清除,但 ClojureScript 不會
以下 ClojureScript 特殊形式與它們的 Clojure 對應形式相同:if
、do
、let
、letfn
、quote
、loop
、recur
、throw
和 try
。
var
註釋
Vars 不會在運行時重新化。當編譯器遇到 var
特殊形式時,它會發出一個 Var
實例,反映編譯時元數據。(這滿足許多常見的靜態用例。)
def
註釋
def
會產生普通的 JS 變數
編譯器不會強制執行 :private
元數據
私有變數存取會觸發分析警告
:const
元數據
將導致內嵌編譯時靜態 EDN 值
導致符號解析為 ^:const
Vars 的 case
測試常數內嵌其值
除非設定 :def-emits-var
編譯器選項(REPL 的預設值為 true
),否則 def
表單會求值為 init 表單的值(而不是變數)
if
註釋
有關 Java 的布林包裝盒的部分在 ClojureScript 中不相關
fn
註釋
目前在呼叫 fn 時,沒有運行時實作對數量的強制執行
monitor-enter
、monitor-exit
和 locking
未實作
ClojureScript 的巨集必須在與使用它們的編譯階段不同的階段中定義。實現此目的的一種方法是在一個命名空間中定義它們,然後從另一個命名空間中使用它們。
巨集通過命名空間宣告中的 :require-macros
關鍵字引用
(ns my.namespace
(:require-macros [my.macros :as my]))
可以使用 Sugar 和其他 ns
變體來代替使用 :require-macros
基本形式;有關詳細資訊,請參閱下面的「命名空間」。
巨集以 *.clj
或 *.cljc
檔案編寫,並在使用常規 ClojureScript 時編譯為 Clojure,或者在使用引導/自我託管 ClojureScript 時編譯為 ClojureScript。需要注意的一點是,基於 Clojure 的 ClojureScript 巨集產生的程式碼必須以 ClojureScript 中的功能為目標。
ClojureScript 命名空間可以從同一命名空間中要求巨集,只要它們保持在不同的編譯階段即可。因此,例如, |
與 Clojure 不同,在 ClojureScript 中,巨集和函數可以具有相同的名稱(例如,cljs.core/+
巨集和 cljs.core/+
函數可以共存)。
您可能會想:「如果是這樣,我會得到哪個?」ClojureScript(與 Clojure 不同)具有兩個不同的階段,它們使用兩個單獨的非交互式命名空間。巨集展開首先發生,因此像 |
列印
目前未實作 *out*
和 *err*
正規表示式支援
ClojureScript 的正規表示式支援與 JavaScript 的支援相同
斷言
在 JVM ClojureScript 中,無法在運行時動態將 *assert*
設定為 false。相反地,必須使用 :elide-asserts
編譯器選項來產生刪除效果。(另一方面,在自我託管的 ClojureScript 中,*assert*
的行為與 Clojure 完全相同。)
nil
雖然在 Clojure 中,nil
與 Java 的 null
相同,但在 ClojureScript 中,nil
等同於 JavaScript 的 null
和 undefined
。
數字
目前 ClojureScript 數字只是 JavaScript 數字
由於目前沒有可以強制轉換的型別,因此未實作強制轉換
字元
JavaScript 沒有字元型別。Clojure 字元在內部表示為單字元字串
關鍵字
ClojureScript 關鍵字不保證是 identical?
,對於快速相等性測試,請使用 keyword-identical?
集合
提供持續性集合
Clojure 實作的移植版本
持續性向量、雜湊對應和雜湊集合中已實作暫時性支援
已實作大多數但非全部的集合函數
StructMaps
ClojureScript 不會實作 defstruct
、create-struct
、struct-map
、struct
或 accessor
。
defprotocol
和 deftype
、extend-type
、extend-protocol
的運作方式與 Clojure 中相同
協定不會像在 Clojure 中那樣重新化,沒有運行時協定物件
某些反射功能 (satisfies?
) 的運作方式與 Clojure 中相同。
satisfies?
是一個巨集,必須傳入一個協定名稱。
extend
目前尚未實作。
specify
,將不可變的值擴展到協定 - 實例層級的 extend-type
,沒有包裝器。
ClojureScript 中的命名空間會編譯成 Google Closure 命名空間,這些命名空間表示為巢狀的 JavaScript 物件。重要的是,這表示命名空間和變數可能會發生衝突 - 但是編譯器可以偵測到這些有問題的情況,並且在發生這種情況時會發出警告。
您目前必須僅使用具有以下注意事項的 ns
表單
您必須使用 :use
的 :only
表單
:require
支援 :as
、:refer
和 :rename
不支援 :refer :all
所有選項都可以省略
在這種情況下,符號可以直接用作 libspec
也就是說,(:require lib.foo)
和 (:require [lib.foo])
都受支援,並且意思相同
:rename
指定從參照的變數名稱到不同符號的映射(可用於防止衝突)
前綴列表 不受支援
:refer-clojure
的唯一選項是 :exclude
和 :rename
:import
僅適用於匯入 Google Closure 類別
ClojureScript 類型和記錄應該使用 :use
或 :require :refer
導入,而不是 :import
巨集必須在與它們被使用的不同的編譯階段中定義。實現此目的的一種方法是在一個命名空間中定義它們,然後在另一個命名空間中使用它們。它們透過 :require-macros
/ :use-macros
選項引用到 ns
:require-macros
和 :use-macros
支援與 :require
和 :use
相同的表單
隱式巨集載入:如果需要或使用命名空間,並且該命名空間本身需要或使用來自其自身命名空間的巨集,則將使用相同的規範隱式需要或使用巨集。此外,在這種情況下,巨集變數可以包含在 :refer
或 :only
規範中。這通常會簡化庫的使用,使得使用命名空間不必擔心明確區分某些變數是函數還是巨集。例如
(ns testme.core (:require [cljs.test :as test :refer [test-var deftest]]))
將導致 test/is
正確解析,同時 test-var
函數和 deftest 巨集可用於非限定方式。
內聯巨集規範:為了方便起見,可以給 :require
提供 :include-macros true
或 :refer-macros [syms…]
。兩者都會分解成顯式載入包含巨集的匹配 Clojure 檔案的表單。(這與被需要的命名空間是否在內部需要或使用其自身的巨集無關。)例如
(ns testme.core
(:require [foo.core :as foo :refer [foo-fn] :include-macros true]
[woz.core :as woz :refer [woz-fn] :refer-macros [apple jax]]))
是以下語法的簡寫
(ns testme.core
(:require [foo.core :as foo :refer [foo-fn]]
[woz.core :as woz :refer [woz-fn]])
(:require-macros [foo.core :as foo]
[woz.core :as woz :refer [apple jax]]))
自動別名 clojure 命名空間:如果需要或使用不存在的 clojure.*
命名空間,並且存在匹配的 cljs.*
命名空間,則會載入 cljs.*
命名空間,並且會自動建立從 clojure.*
命名空間到 cljs.*
命名空間的別名。例如
(ns testme.core (:require [clojure.test]))
將會自動轉換為
(ns testme.core (:require [cljs.test :as clojure.test]))
現有的 Clojure 程式庫必須符合 ClojureScript 的子集才能在 ClojureScript 中運作。
此外,Clojure 程式庫中的巨集必須可以編譯為 ClojureScript,才能透過其 cljs.js/*load-fn*
功能在自我託管/引導的 ClojureScript 中使用。
def
和 binding
的運作方式與 Clojure 中相同
但是在普通的 js 變數上
Clojure 可以表示未綁定的變數。在 ClojureScript 中,(def x)
會導致 (nil? x)
為 true
。(在這種情況下,(identical? nil x)
為 false
,但 (identical? js/undefined x)
為 true
。)
在 Clojure 中,def
會產生變數本身。在 ClojureScript 中,def
會產生值,除非設定了 REPL 選項 :def-emits-var(對於 REPL,這預設為 true
)。
Atoms 的運作方式與 Clojure 中相同
目前尚未實作 Refs 和 Agents
驗證器的運作方式與 Clojure 中相同
未實作 intern
- 沒有具體化的變數
主機語言互通功能(new
、/
、.
等)在可能的情況下,運作方式與 Clojure 中相同,例如
goog/LOCALE
=> "en"
(let [sb (goog.string.StringBuffer. "hello, ")]
(.append sb "world")
(.toString sb))
=> "hello, world"
在 ClojureScript 中,Foo/bar
始終表示 Foo
是一個命名空間。它不能用於 Clojure 中常見的 Java 靜態欄位存取模式,因為 JavaScript 中沒有反射資訊來判斷這一點。
特殊的命名空間 js
提供對全域屬性的存取
js/Infinity
=> Infinity
若要存取物件屬性(包括您想要作為值的函數,而不是執行),請使用前導連字號
(.-NEGATIVE_INFINITY js/Number)
=> -Infinity
編譯與 Clojure 不同
所有 ClojureScript 程式都會編譯成(可選擇最佳化的)JavaScript。
可以將個別檔案編譯成個別的 JS 檔案以分析輸出
生產編譯是透過 Google Closure 編譯器的全程式編譯
在 ClojureScript 中,gen-class
、gen-interface
等是不必要且未實作的
ClojureScript 目前包含以下從 Clojure 移植過來的非核心命名空間
clojure.set
clojure.string
clojure.walk
clojure.zip
clojure.data
clojure.core.reducers
fold
目前是 reduce
的別名
cljs.pprint
(clojure.pprint
的移植版本)
cljs.spec
(clojure.spec
的移植版本)
cljs.test
(clojure.test
的移植版本)
Clojure 和 ClojureScript 共享相同的 貢獻者協議和開發流程。