3

I'm debugging some code, and have boiled my problem down to this one simple example:

(js/console.log (.-hey (clj->js {:hey "please work!"})))

prints undefined

It must have something to do with renaming during advanced optimizations, but I can't figure out what's going on, nor how to fix it...

If you use an online CLJS REPL, like http://clojurescript.net/, it works as expected (returns nil but prints please work!), but under advanced optimizations using cljsbuild, it doesn't work at all!

Hrmm, any idea where I'm botching this up?

Edit: Upon further reflection, and some advice from clojurians on slack, this is because the optimization mangles the keywords, I think, when I use clj->js.

I find that I (think) I need to do this though since I'm trying to interop with the D3.js library (from cljsjs), and when I pass it things, it's expecting js objects, not cljs objects. Is there an idiomatic way of passing in cljs objects to a js library that you're interoping with? (If that is indeed my problem?)

4
  • 1
    Hmmm have you tried (aget (clj->js ...) "hey") or #js {"hey" ...? Commented Feb 20, 2016 at 19:18
  • Check out cljsjs.github.io for externs for D3. Commented Feb 21, 2016 at 7:13
  • @TimothyPratley I think that would work, but I ended up changing the map to a vector/array, since indexes don't get mangled, and I'm using aget for the index of the vector. That works too, thanks for the response! Commented Feb 21, 2016 at 22:34
  • Good thinking! Glad you solve it. Commented Feb 22, 2016 at 1:22

1 Answer 1

5

Your code won't work when compiled with advanced optimizations. You can check it comparing versions compiled with different optimization settings.

The part producing JS object works correctly, e.g. following will work:

(.log js/console (clj->js {:hey "please work"}))

will produce

Object {hey: "please work"}

But the part accessing hey property will break during compilation with advanced optimizations.

When you compile

(.log js/console (.-hey (clj->js {:hey "please work"})))

you will get something similar:

With optimizations :none:

console.log(
  cljs.core.clj__GT_js.call(
    null,new cljs.core.PersistentArrayMap(null, 1,
      [new cljs.core.Keyword(null,"hey","hey",301812684),"please work"], null)
  ).hey);

With optimizations :advanced:

console.log(Tg(new jb(null,1,[Nh,"please work"],null)).ci);

Notice how the .hey property key got mangled into .ci. This is because Google Closure renamed it during the optimizations phase (to save space in the result JS file it replaces names with shorter identifiers). "hey" string in your object won't get mangled as it's a string literal and you get the inconsistence.

When you pass the result of clj->js to an external function (e.g. in d3js) your code should work as expected as the external library won't be mangled if you use minified version with externs file (see @Andre's comment to your question and CLJS doc about deps and externs.)

When you need to access such properties like in your examples and need it to work with advanced optimizations you can use goog.object/get or goog.object/getValueByKeys as described in cljs.core/aget doc.

Sign up to request clarification or add additional context in comments.

2 Comments

Great advice. Also, here's another solution (similar) that I ended up going with: I ended up changing the map to a vector/array, since indexes don't get mangled, and I'm using aget for the index of the vector. That works too, thanks for the response!
I would like to add that the proper closure compiler way to take care of this is goog.object/get or goog.object/getValueByKeys, and aget is considered incorrect unless working directly with arrays.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.