Better way to deal with js-objects?

I’m working on a game using cljs + expo + three.js, and due to performance reasons I’ve decided to use js-objects in situations where the data is “simple”. Essentially I keep the game state in a hash-map, and then I keep references and settings as js-objects on the “game objects” (THREE/Meshes). This is because to my understanding and from my measurement, (.-a #js {:a 10}) is a lot faster than (:a {:a 10}). Since I’m trying to keep 60fps, even fractions of milliseconds can add up, and these values are accessed often.

However, some functions are now getting pretty stupid. I feel that having to hard code all .- calls aren’t the right way to go. Here’s an example:

(defn go->zone-go
  [go]
  (and go
       (or
        (and (.. go -state)
             (.. go -state -cardParent)
             (.. go -state -cardParent -state)
             (.. go -state -cardParent -state -zoneId)
             (.. go -state -cardParent))
        (and (.. go -parent)
             (.. go -parent -state)
             (.. go -parent -state -zoneId)
             (.. go -parent))
        (and (.. go -state)
             (.. go -state -zoneId)
             go))))

I felt there ought to be a better way to write this, e.g. a macro, that lets me write this: (js-> go -state -cardParent -state -zoneId) which would expand to:

(and (.. go -state)
     (.. go -state -cardParent)
     (.. go -state -cardParent -state)
     (.. go -state -cardParent -state -zoneId)

I’ve looked around a bit but haven’t been able to figure out which libraries doesn’t sacrifice performance.

Thankful for any thoughts and tips. I’m still just a beginning cljs writer.

1 Like

Is that and just to assert not null?

Do you just want to use some->?

(some-> go .-state .-cardParent .-state .-zoneId)

3 Likes

Thanks didibus, that makes a lot of sense, haha.

You might also want to check out the cljs-oops library, which provides a couple of handy functions for working with JS objects. You could use oget:

(oget go "state" "cardParent" "state" "zoneId")

By default (i.e. :optimizations :none in ClojureScript compiler settings), it generates diagnostics code to warn you about missing keys – you can prefix the keys with “?” to disable the warnings.

(oget go "?state" "?cardParent" "?state" "?zoneId")

With advanced compilation (:optimizations :advanced), the diagnostics code is not generated and instead it generates fast JS object access code, basically the same that you’d get with (.. go -state ...) or with (some-> go .-state ...).

2 Likes

A few more options: https://github.com/mfikes/cljs-bean and https://github.com/appliedsciencestudio/js-interop