What follows is a section-by-section review of the sections on the left-hand navigation panel of http://clojure.org, enumerating what is different in ClojureScript and in some cases the similarities.
A deeper discussion on ClojureScript's rationale can be found elsewhere on this site.
Same as Clojure. Clojure's identity model is simpler and more robust than mutable state, even in single threaded environments.
ClojureScript's REPL can be launched from within the Clojure REPL. See Quick Start for details.
ClojureScript has the same immutable persistent collections as Clojure on the JVM.
ClojureScript's macros are written in Clojure, and are referenced via
require-macros keyword in namespace declarations:
(ns my.namespace (:require-macros [my.macros :as my]))
One point of note is that the code generated by ClojureScript macros must target the capabilities in ClojureScript.
Clojure's model of values, state, identity, and time is valuable even in single-threaded environments.
bindingis similar to that in Clojure
defproduces ordinary JS variables
See Quick Start
\produces a single-character string.
read-stringfunctions are located in the
mainsupport is currently not implemented
evalis not supported in ClojureScript
The following ClojureScript special forms are identical to their Clojure
varspecial form it emits a
Varinstance reflecting compile time metadata. (This satisfies many common static use cases.)
defproduces ordinary JS variables
:privatemetadata is not enforced by the compiler
:constmetadata in ClojureScript disallows re-
case. In Clojure
defbut value inlined at use site.
defform evaluates to the value of the init form (instead of the var)
lockingare not implemented
Macros are written in Clojure. (See the Lisp differences section above).
Unlike in Clojure, in ClojureScript a macro and a function can have the same name (for example the
cljs.core/+ macro and
cljs.core/+ function can coexist).
Note: You may be wondering: “If that's the case, which one do I get?” ClojureScript (unlike Clojure) has two distinct stages that make use of two separate non-interacting namespaces. Macroexpansion occurs first, so a form like
(+ 1 1)initially involves the
cljs.core/+macro. On the other hand, in a form like
(reduce + [1 1]), the
+symbol is not in operator position, and passes untouched through macroexpansion to analysis/compilation where it is resolved as a the
*out*is currently not implemented
*assert*to false at runtime. Instead the
:elide-assertscompiler option must be used to effect elision.
identical?, for fast equality testing use
extend-protocolwork as in Clojure
satisfies?) work as in Clojure
satisfies?is a macro and must be passed a protocol name
extendis not currently implemented
specify, extend immutable values to protocols - instance level
Works as in Clojure.
nsform only with the following caveats
(:require [lib.foo])are both supported and mean the same thing
:importis available for importing Google Closure classes
:require :refer, not
:use-macrossupport the same forms that
Implicit macro loading: If a namespace is required or used, and that namespace itself requires or uses macros from its own namespace, then the macros will be implicitly required or used using the same specifications. This oftentimes leads to simplified library usage, such that the consuming namespace need not be concerned about explicitly distinguishing between whether certain vars are functions or macros.
Inline macro specification: As a convenience,
:require can be given either
:include-macros true or
[syms...]. Both desugar into forms which explicitly load the matching Clojure file containing macros. (This works independently of whether the namespace being required internally requires or uses its own macros.) For example:
(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]]))
is sugar for
(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]]))
Existing Clojure libs will have to conform to the ClojureScript subset in order to work in ClojureScript
bindingwork as in Clojure
internnot implemented - no reified Vars
Refs and transactions are not currently supported.
Agents are not currently supported.
Atoms work as in Clojure.
The host language interop features (
., etc.) work as in Clojure where possible, e.g.:
goog/LOCALE => "en" (let [sb (goog.string.StringBuffer. "hello, ")] (.append sb "world") (.toString sb)) => "hello, world"
Foo/bar always means that
To access object properties (including functions that you want as a value, rather than to execute) use a leading hyphen:
(.-Infinity js/window) => Infinity
^double—when used on function parameters—are type declarations in Clojure, they are type hints in ClojureScript.
Compilation is different from Clojure:
gen-interface, etc. are unnecessary and unimplemented in ClojureScript
ClojureScript currently includes the following non-core namespaces ported from Clojure:
foldis currently an alias for
ClojureScript is the same (at a Lisp level) as Clojure except for the following:
Clojure and ClojureScript share the same Contributor Agreement and development process.