-
Notifications
You must be signed in to change notification settings - Fork 16
Notes
I have tried to tune the macros so that they emit code (via :advanced compilation) that's similar to what you might write by hand in javascript, so that you can use this even for relatively performance-sensitive code.
For very performance-sensitive contexts, you can forego null-safety by using "unchecked" operations, which will throw errors if passed nil
. Two are available in js-interop: j/unchecked-get
and j/unchecked-set
, used as follows:
(j/unchecked-get o :someKey) ;; returns `o`
(j/unchecked-get o .-someKey) ;; works with renamable keys
(j/unchecked-set o :a 10 :b 20) ;; can set multiple properties at once
(j/unchecked-set o .-a 10 .-b 20)
These are not recommended unless you really need them.
The general rule is that you should use "renamable" keys (ie. use dot-syntax, like .-someKey
) for code that is run through the Closure compiler, which usually means ClojureScript code.
Objects created using js literal syntax, like #js:{:hello "world}
, do not have renamable keys, so you should use string/keyword keys to access them. Similarly, objects created from parsing JSON, for example data that you fetch from an API, are also not renamable.
(-> (fetch-post ...)
(.then (fn [post] (j/get post :title))))
It's possible to implement ILookup
for "all" JavaScript objects, as explained by Mike Fikes here. This will only have an effect on plain objects whose prototype is js/Object
, not for any other kind of object (eg. it won't work for browser events or React components). It also mutates the global js/Object
.
How it's done:
Associative destructuring is based on get, which can be mapped onto goog.object/get for JavaScript objects by extending them to ILookup:
(extend-type object ILookup (-lookup ([o k] (goog.object/get o (name k))) ([o k not-found] (goog.object/get o (name k) not-found))))
One thing js-interop does that is simply not possible using other tools is provide "lookup" semantics for host-interop (dot) keys. Using this feature looks like this: (j/get o :someKey :not-found)
. Following Clojure's lookup semantics means that we must return the :not-found
value when :someKey
is not contained by the object, regardless of what value :someKey
may have, even if it is nil
. The presence of a key is distinguished from its value.
Implementing this requires using the Closure Compiler reflection tool goog.reflect/objectProperty
. At compile time, it can tell us what a given key will be renamed to. An expression like (.-someProperty x)
is going to compile to something like x.Gx
. In this case, the reflect/objectProperty
function would tell us that someProperty
=> Gx
.