-
Notifications
You must be signed in to change notification settings - Fork 790
Working with Javascript classes
Most ClojureScript developers will at some point have to work with existing JavaScript libraries or frameworks which require an OOP approach. So you sometimes have to work with Javascript "classes" (ie. prototype
hierarchies) from within your ClojureScript.
To create a class Bag
with setter functions add
and print
, you might first come up with something like the following (which looks ugly as hell). There are thankfully much cleaner ways to achieve the same effect as we will demonstrate below.
(defn Bag []
(this-as this
(set! (.-store this) (array))
this))
(set! (.. Bag -prototype -add)
(fn [val]
(this-as this
(.push (.-store this) val))))
(set! (.. Bag -prototype -print)
(fn []
(this-as this
(.log js/console (.-store this)))))
(def mybag (Bag.))
(.add mybag 5)
(.add mybag 7)
(.print mybag)
You can use protocols to provide namespaced methods and a more idiomatic syntax:
(defprotocol MyBag
(add [this val])
(print [this]))
(extend-type Bag
MyBag
(add [this val]
(.push (.-store this) val))
(print [this]
(.log js/console (.-store this))))
(def mybag (Bag.))
(add mybag 2)
(add mybag 3)
(print mybag)
You can also use deftype
and the special Object
protocol.
(deftype Bag [store]
Object
(add [_ x] (.push store x))
(print [_] (.log js/console store)))
(defn bag [arr] (Bag. arr))
The Object
protocol can also be used with reify
for a pure functional solution.
(defn bag [store]
(reify
Object
(add [this x] (.push store x))
(print [this x] (.log js/console store))))
If you want some state to be fully encapsulated and kept private, you can refactor the constructor function as follows.
(defn bag []
(let [store (create-store)]
(reify
Object
(add [this x] (.push store x))
(print [this x] (.log js/console store)))))
This makes store
a local variable (closure), set by calling the function create-store
(which you must define). Happy Clojure!
Many of the latest front-end frameworks such as Ember, Angular and Aurelia are already leveraging ES7 decorators. To get a good overview of decorators, check out this post
We could generate decorator functionality via one or more macros. Info on ClojureScript macros can be found here. Alternatively it could perhaps be done by adding decorator metadata to functions and types, and then post-process such functions.
We leave it as an exercise to the community (including you) to come up with recipes to achieve commonly used ES6/ES7 functionality such as decorators, while staying within the bounds of current ClojureScript limits (which compiles to ES3 compatible JavaScript as of 2016).
- Rationale
- Quick Start
- Differences from Clojure
- [Usage of Google Closure](Google Closure)