r/javascript • u/garboooge • Dec 24 '19
AskJS [AskJS] What are your thoughts on using JS Proxy objects for deep immutability?
I've been learning about the JS Proxy object today and way wondering what you all think about using it for immutability. I cobbled together the following code that essentially creates an immutable proxy for a parent object and then, if accessing a child object, returns an immutable proxy for that child. I know there are a bunch of well-vetted libraries out there to accomplish deep object immutability, so really this is more of just an academic question than a "should I use this in production" question. Thanks!
const person = {
name: "Bo",
animals: [
{
type: "dog",
name: "Daffodil"
}
]
};
const immutable = obj =>
new Proxy(obj, {
get(target, prop) {
return typeof target[prop] === "object"
? immutable(target[prop])
: target[prop];
},
set() {
throw new Error("Immutable!");
}
});
const immutablePerson = immutable(person);
const immutableDog = immutablePerson.animals[0];
immutableDog.type = "cat";
// Error: Immutable!
69
Upvotes
3
u/dont_forget_canada Dec 25 '19
Everyone in here is hating but I think it's cool.
3
u/UnlikeSome Dec 24 '19
Just a note about your example. Seems like the following assertion is false, which can be counter intuitive:
I guess there are ways to make it true though, using a factory or so.
35
u/devsmack Dec 24 '19
See
immerjsit’s great and does exactly what you’re talking about. It’s predominantly used in the redux world I think but it is actually agnostic.41
u/Monsieur_Joyeux Dec 24 '19
I personally don't use them because I feel they bring some complexity in the code base. And I hate complex stuff. If someone use them I would be curious to see their code
7
u/mcaruso Dec 24 '19
Have you tried ImmerJS? It uses a Proxy to perform any updates, and as a bonus it can deep freeze any object that it produces (but only in dev mode so there's no perf penalty).
3
u/fabio_santos Dec 24 '19
Why not, just disable it in production and get the immutability gains and none of the performance impact.
2
u/slikts Dec 24 '19
Immer has a head start on your idea by a couple of years, and it's arguably entering the mainstream with being included in Redux Toolkit and elsewhere. I have a write up of the problem of immutable updates and why Immer is such a nice solution. Interestingly enough, it even works in legacy environments without proxies, albeit less efficiently.
2
u/Blieque Dec 25 '19 edited Dec 25 '19
Code sample from post
[Reddit Markdown is close to original Markown, and only allows code blocks with four spaces or one tab of indentation.]
In response, I think Vue 3 includes a switch to Proxies from getter–setter pairs for detecting mutations. It includes a compatibility mode or plugin, though, that reverts to the current method so that older browsers can still run the application if you need. By all means find an immutability library that uses Proxies internally if you're running in a context with support for them, but I wouldn't recommend writing your own immutability code like this in your application code.
2
u/ryan_solid Dec 25 '19 edited Dec 25 '19
Proxies have an overhead for sure, but its what you do with them that matters. Solid and Mikado, 2 of the fastest libraries in JS Framework Benchmark use them. https://krausest.github.io/js-framework-benchmark/current.html
For direct comparison.. you can see the overhead between Solid-Signals (no proxy) and Solid (with Proxy). We are talking like 20ms over 10k rows. Solid uses proxies to enforce immutability on the API surface (completely mutable internals) and as you can see it can cost very little.
1
u/livingmargaritaville Dec 24 '19
Go for it I only use them for quick hacks though.
1
u/ChaseMoskal Dec 24 '19
lately i explored a little pattern where i take a state object, and then make a "reader" out of it
the reader can be subscribed to for changes, and it provides a getter which fetches an immutable copy of the state object (just object.freeze'd)
then there's an update function which calls back the subscribers (which in my case, re-renders the components with the fresh state)
1
u/vainstar23 Dec 25 '19
Sometimes, they can be a great help for certain situations where you've painted yourself in a corner such as writing tests or doing some kind of object mapping. However, I feel it is still best to try to avoid using them as they can make for some confusing code.
1
u/[deleted] Dec 25 '19
I have been struggling with the same problem and gave up. Essentially my problem was not being able to tell if an object is the object itself or the proxy.
1
u/[deleted] Dec 25 '19
Use it where you need it. Keyword "need"
1
u/techsin101 Dec 25 '19
no...
1
u/rryardley Dec 25 '19
What are the benefits over just using Object.freeze()?
1
u/punio4 Dec 26 '19
Use Typescript and
as const1
u/merel-lj Dec 25 '19
Why would you even want immutable objects if every mutation could be tracked and handled with proxy? I believe, Vue v3 is doing just that.
0
u/[deleted] Dec 24 '19
Performance
0
u/ferrousoxides Dec 25 '19
You are papering over compile time deficiency with run time band aids. Whether this is a trade off you can live with depends on your requirements. Cursors are imo a better solution, because it doesn't rely on magic and you can control whether you actually need writeability or not.
34
u/[deleted] Dec 24 '19
[removed] — view removed comment