Skip to content
Matt Huebert edited this page Mar 7, 2019 · 4 revisions

On Performance

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.

On renamable vs. static keys

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))))

Extending the global object

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))))

On Lookup semantics

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.

Clone this wiki locally