Personally I just use TypeScript which can enforce not mutating at compile time (for the most part).
Personally I just use TypeScript which can enforce not mutating at compile time (for the most part).
Part of the immutable value proposition is being able to work with the objects. Based on [0] Freezing feels more like constant than immutable. And the 'frozenness' isn't communicated through the language - I could be passed a frozen or unfrozen object and I wouldn't know without inspecting it.
And freeze isn't recursive against the entire object graph, meaning the nature of freezing is entirely dependent on the implementation of that object.
I really like the language-level expression and type checking of Rust. But it does require intentional language design.
I'm not criticising JS (though I think there are plenty of far better langauges). Just saying that calling `freeze` 'immutable' isn't the full story.
[0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
In my experience most "immutability" in JS is enforced by convention or, at best, static type systems. It's not ideal, but it works.
I suppose that still only fits in the "immutable by convention" category, though.
const foo = Object.freeze({ a: 1, b: 2 })
const fooCopy = { ...foo }
And you are right that Object.freeze doesn't work recursively (although making it work recursively is fairly easy to implement yourself if you use it a lot).But like it or not JS isn't a language with a powerful type system, and it doesn't pretend to have one so knocking it for that is like knocking Python for using whitespace, or knocking Rust for needing a compiler.
Luckily, Typescript and Flow have most of what you are asking for, and they work pretty damn well across the entire ecosystem.
Off the top of my head, I know typescript has the ability to mark things as read-only even at the individual property level. [1] And they have tons of the type checking nice-ness that you can expect from other "well typed" languages like Rust.
[1] https://basarat.gitbooks.io/typescript/docs/types/readonly.h...
Yes, although the new object is not frozen by default. Adding is quite straight forward, expecially with the spread syntax
let x = { a: 1, b: 2 };
Object.freeze(x)
let y = {...x, b: 3}
// y == { a: 1, b: 3 }
Removing is less intuitive let x = { a: 1, b: 2 };
Object.freeze(x)
let { b, ...y} = x;
// y == { a: 1 }
> And the 'frozenness' isn't communicated through the languageYes, but given that JS is a dynamic language I wouldn't expect anything different (everything must inspected at runtime).
> And freeze isn't recursive against the entire object graph
You're right, although one could quickly implement a recursive version.
In any case I find Object.freeze not much useful since trying to mutate a frozen object will simply ignore the operation; I think that most of the time trying to do that should be considered an error and I would prefer to have and exception raised.