
Show HN: An isomorphic, configurable JavaScript utility for object deep cloning - jfet97
https://github.com/jfet97/omniclone
======
smohare
My background is in pure mathematics, where an isomorphism is a structure
preserving map between two objects. The description “an
isomorphic...JavaScript function” does not make grammatical sense to me,
unless the implication is that this function is isomorphic to some standard
clone/copy method somehow, or that it is an automorphism on some set of
JavaScript objects.

Is the word being used in some other sense?

~~~
spion
Yes, in the silly sense that the code can run in both nodejs and a browser.
The unfortunate article that coined it: [https://blog.nodejitsu.com/scaling-
isomorphic-javascript-cod...](https://blog.nodejitsu.com/scaling-isomorphic-
javascript-code/)

~~~
throwawaymath
I'm not generally a fan of litigating language choices since, well, all words
are made up after all. It's easy to be accused of pedantry if you're going to
complain about someone misusing mathematical terminology in an orthogonal
context (see what I did there?). It's hard to build a compelling soapbox on,
"Well actually, that terminology isn't correct..."

That being said, I'm a little baffled after reading that article. I suppose
the thinking was that the client and server are isomorphic to each other if
they're running the same code. As someone who also has a mathematical
background I don't think it's very elegant...but it's obviously caught on and
it's creative, so I can dig it.

~~~
jacobolus
“isomorph” is just Greek for “same form”.

I don’t think mathematics should get a monopoly on this one. Though it can
probably keep “isomorphism” to itself, since “morphism” is as far as I know a
made up math term.

------
fiallega
I am sure before you started this project you looked at other existing
libraries. How is this solution compared with the lodash _.cloneDeep
[https://lodash.com/docs/4.17.11#cloneDeep](https://lodash.com/docs/4.17.11#cloneDeep).

Thanks

~~~
jfet97
No I've built it on my own. I only know that popular libraries as lodash don't
call constructors, so I think there is no way to mantain a proper
[[Prototype]] nor the constructor props. IDK how other libraries do to handle
circ references nor if them allow to copy non-enums props and getters&setters.

~~~
porphyrogene
You don’t need to use the constructor syntax to have an object inherit
prototypes from another.

~~~
erikpukinskis
How do you do it without a constructor? Iterate over the properties on the
prototype?

~~~
porphyrogene
The global Object’s assign method is the “old” way to inherit prototypes. ES6
syntax did not add new language features; they were there all along.

~~~
fiallega
Thanks a lot for all clarifications.

~~~
porphyrogene
No problem. A good exercise I used to do was writing inheritence patterns
first with pre-ES6 syntax, then constructors, then classes. They are, of
course, interchangeable but it’s not wise to rehearse an anti-pattern.

EDIT: I said assign method earlier, I meant create. The assign method is the
pre-ES6 way of creating object instances. Object.create() is used to inherit
prototypes. That’s such a common mix-up that I’m embarrassed I didn’t catch
it.

~~~
erikpukinskis
I just don’t use ES6. It’s amazing. I have such a smaller language surface,
and so much more predictable and debuggable code. Not a popular approach
though.

(Of course I can and do use ES6 when coding for other people, I write to
whatever standard is established in a codebase.)

------
yogthos
Meanwhile, using persistent data structures instead of objects avoids the
problem space entirely.

~~~
jfet97
Yes but with persistent data structures each operation implies a cost, more or
less big. For example most of times you do something with an persistent array
you end up with fully cloning it. What's the point? I know the advantages of
immutability, but I think that perform impure operations into pure functions,
in a safe contest, is not so bad. My function can help with this...we could
perform those faster impure operations (modifiyng local objects and arrays)
and then create full, deeply cloned entities to pass around.

~~~
yogthos
So my day job is working with Clojure/Script that defaults to immutability,
and my team builds fairly large apps, here's a talk describing one of them
[https://www.youtube.com/watch?v=aXBe6hoi-
Mw](https://www.youtube.com/watch?v=aXBe6hoi-Mw)

In almost a decade of using Clojure, I've never seen persistent data
structures become the bottleneck. The difference in cost is O(1) for in place
mutation vs O(log 32 n) for revisioning. Note that the large exponent means
the tree ends up being very flat, so updates end up being quite cheap in
practice. Most times when I do something to a persistent data structure, I
only change a small portion of the overall data structure. In fact, I think
it's quite rare that you'd end up cloning the whole thing.

Personally, I view mutability as an optimization, and I think it should be
used in cases where you actually need the performance beyond what persistent
data structures can offer. My experience is that such cases are far and few
between.

------
ThePhysicist
I found that often when you want clean deep cloning of objects (e.g. for state
data) and when you’re in control of creating the objects it’s better to build
them out of Map and Array instances instead of the builtin object type. We use
this extensively for config structures and state data, and it makes cloning or
merging objects much easier (as you only need to differentiate between a map,
array and everything else). This assumes that you will not clone any of the
“object” types in your data structure though, so you might want to store
immutable data types on there only (which often is possible and advisable for
config and state data though). The downside is that the syntax for creating
and working with maps is quite ugly (IMHO), I hope they will improve this in a
future version of JS.

~~~
jfet97
hoping that my funtion can help you :)

------
jph
Great tool, thank you for building it and sharing it. I especially like your
configurability among what to clone or not, and your good handling of so many
edge cases.

IMHO the concept of "deep clone" is a design pattern and a potential language
improvement idea. An example is "How does this deep clone handle external
items, or transient items, or circular items?" Your project shows (correctly
IMHO) that there many be many choices, and also reasonable defaults.

~~~
jfet97
I've really appreciated your comment :D

------
gitgud
This doesn't seem to copy methods on an object. Why is this better than using
JSON.stringify(OBJ) and JSON.parse(OBJ) to deep clone?

~~~
jfet97
Lot of stuff, as you can see in the docs. JSON.parse/stringify does not call
the constructors for example

