Starting with an appeal to authority:
So that’s David and Mike’s opinion, now I want to look into the tradeoffs.
Firstly, I’ll try to make a clear distinction between JS data vs API: A data object is any object you could round-trip through JSON/stringify => JSON/parse. An object that may appear to be a data object because it only contains properties (ie no methods), may not be a data object, because those properties might be getters or setters. If the object came via a JS library, it’s most likely not a data object unless the library documentation explicitly says so.
That definition should be good enough for the purposes of this post, let’s ignore prototypes etc for now.
I find the
(.-length "foo") item in Mike’s list interesting.
It’s clear in the case of the string
"foo" that this is not a data object
goog.object/get will not work to access
length, or any other property of a string.
But consider this example though:
(goog.object/get #js "length")
0, so demonstrating it is possible to use the
goog.object API to access API properties.
So, we have what appears to be just a stylistic choice between that and
(.-length #js). Why choose either one?
It’s easy to see that the goog.object one will survive advanced compilation, whereas in some
cases the dot-access would need a type hint, for example:
(.-length ^js foo).
So +1 for the goog.obj approach so far I guess.
In working with the API of some JS object, it’s quite common to both access properties and call methods:
(let [foo ^js (some-fn) bar-prop (.-bar foo)] (.methodFoo foo (inc bar-prop)))
As I pointed out before, we could have accessed
this example is being consistent in using dot access only for the API of
foo. We could also have accessed
goog.object/get (and then invoked it), but I don’t think it would be idiomatic to do so.
So, Following a rule ‘dot access only for APIs’ means the code makes a clear statement that it is
working with API, not data - regardless of whether it is just properties, or both methods and properties we need to
use. This comes as the cost of having to remember to put type hints in. I’ve come to think
type hints aren’t so bad, because now type hints are documented
I think it’s easy enough to understand you just need to add
^js when you first see the js object in scope.
Yet another approach would be to use a library such as js-interop. Using that,
you would write
(j/call foo :bar 1) for the js equivalent
foo.bar(1). Type hints are not needed if you use that
library, so it’s definitely an alternative to consider.
So, now that’s all cleared up, which dot-access is preferred,
(.. a -b -c) …?