Today we are going to talk about ClojureScript and review many useful functions and concepts you need to know when working with Clojure/JavaScript interoperability.
To get started you would need a ClojureScript Repl running. On Mac OS and Linux systems I recommend to install Planck.
You can even practice with ClojureScript on your mobile phone with Replete REPL app. It is available for both Apple and Android platforms.
With a ClojureScript Repl running we can now start to experiment. In ClojureScript to access JavaScript functions we have a special namespace js
. For those coming from JavaScript js
is the same as window
object in a browser with all builtin functions(methods) attached to it. For ex.: (js/alert "Hello world!")
or (js/console.log "Hello world!")
.
In the example below we will print “Hello world” with a delay of 1000 ms using a setTimeout function:
(js/setTimeout #(println "Hello world") 1000)
To create a JavaScript object we can use js-obj
function:
cljs.user=> (js-obj "zip" 10001 "city" "New York")
#js {:zip 10001 :city "New York"}
Alternatively we can use #js
macros:
cljs.user=> #js {:zip 10001 :city "New York"}
#js {:zip 10001 :city "New York"}
To delete a property from the object we use js-delete
function. In the function call we need to provide an object name and the key. We need to remember that in JS world keys are strings not keywords:
cljs.user=> (def location #js {:zip 10001 :city "New York"})
#'cljs.user/location
cljs.user=> (js-delete location "zip")
true
cljs.user=> location
#js {:city "New York"}
To create a JavaScript array we use #js
macros again or an array
function:
cljs.user=> #js ["Apples", "Bananas", "Apricots"]
#js ["Apples" "Bananas" "Apricots"]
cljs.user=> (array "Apples", "Bananas", "Apricots")
#js ["Apples" "Bananas" "Apricots"]
Also we could create an array with a desirable length with make-array
function. In the example below we are creating array with five empty elements:
cljs.user=> (make-array 5)
#js [nil nil nil nil nil]
We can convert data structures between JavaScript and Clojure using a js->clj
function:
cljs.user=> (js->clj location)
{"city" "New York"}
Unfortunately keys are still strings after the conversion. To change them back to keywords we need to provide an optional parameter :keywordize-keys
with value of true
:
cljs.user=> (js->clj location :keywordize-keys true)
{:city "New York"}
A clj->js
function is used to convert Clojure data structures to JS ones:
cljs.user=> (clj->js {:zip 10001 :city "New York"})
#js {:zip 10001, :city "New York"}
We can get a property value from the JS object with .-key
syntax. A dot dash .-
is used to distinguish a reading of the property from a calling of the method of the object with .
:
cljs.user=> (.-city location)
"New York"
Another way to read the property of the object is by using aget
function:
(aget location "city")
"New York"
To write a value to the object property we use aset
function:
cljs.user=> (aset location "city" "Boston")
"Boston"
Reading a value of the nested properties is quite simple too. We use ..
to get to the nested structure:
cljs.user=> (def location #js {:address #js {:zip 10001 :city "New York"}})
#'cljs.user/location
cljs.user=> (.. location -address -zip)
10001
Down below is an example of two ways of running now
method on a JS Date
object using a dot notation:
cljs.user=> (js/Date.now)
1618189077370
cljs.user=> (.now js/Date)
1618189084328
To call a function constructor to create an instance of the JS object we use Objectname.
dot notation. It is the same as calling new Date
in JavaScript:
cljs.user=> (js/Date.)
#inst "2021-04-12T01:13:57.709-00:00"