Why would I need to freeze an object in JavaScript?
Asked Answered
I

14

52

It is not clear to me when anyone would need to use Object.freeze in JavaScript. MDN and MSDN don't give real life examples when it is useful. I get it that an attempt to change such an object at runtime means a crash. The question is rather, when would I appreciate this crash?

To me the immutability is a design time constraint which is supposed to be guaranteed by the type checker.

So is there any point in having a runtime crash in a dynamically typed language, besides detecting a violation better later than never?

Insurer answered 9/2, 2013 at 20:35 Comment(8)
Immutability is orthogonal to static typing. If your program expects/requires that an object is not altered, then freezeing the object will guard against code that [incorrectly] attempts to alter it.Corroborate
@bonomo It is orthogonal, actually. Let's take a language like Java. And, say the List<T> interface. Let's say we create a new mutable object, say, l = new ArrayList<X>. Now, let's say we do Collections.unmodifiableList(l) which returns a new list, also of List<T>. However, this new list is immutable while the original list was mutable. Both conform to List<T>.Corroborate
@bonomo As for as the latter comment, 1) it will only throw an exception if in strict mode and 2) would it be better if the program "continued" while leaving the object/system in a state that may be ill-defined?Corroborate
@bonomo While static typing can make it so that an "immutable type" is more easily defined, it is not directly related to object mutability/immutability. Almost every static language - with exceptions like Haskell - supports mutability, while there are dynamically typed languages like Scheme that eschew mutability.Corroborate
@bonomo Likewise, consider this "immutable object" defined in Javascript: f = function (x) { return { value: function () { return x } } }; o = f(42) - without changing the function named by o.value (which would be prevented by freeze), there is no way to change what o.value() evaluates to; hence there is immutability at the semantic level. Before arguing that this doesn't show a type exposing an immutable interface, consider Mock'ing that is sometimes done in statically typed languages.Corroborate
In conclusion: I prefer static typing and immutable objects, but static typing does not inherently guarantee or prevent mutability.Corroborate
@AlekseyBykov so yet another reason to 'use strict'; in all code where you want to find bugs while still developing it rather than after it’s causing some subtle bug in production ;-).Oneiric
I think it’s mainly a tool to defend against people who don’t know what they’re doing, and if you work with disciplined devs, you won’t ever need itGreatgranduncle
K
20

The Object.freeze function does the following:

  • Makes the object non-extensible, so that new properties cannot be added to it.
  • Sets the configurable attribute to false for all properties of the object. When - configurable is false, the property attributes cannot be changed and the property cannot be deleted.
  • Sets the writable attribute to false for all data properties of the object. When writable is false, the data property value cannot be changed.

That's the what part, but why would anyone do this?

Well, in the object-oriented paradigm, the notion exists that an existing API contains certain elements that are not intended to be extended, modified, or re-used outside of their current context. The final keyword in various languages is the most suitable analogy of this. Even in languages that are not compiled and therefore easily modified, it still exists, i.e. PHP, and in this case, JavaScript.

Kunkle answered 9/2, 2013 at 20:38 Comment(2)
I think the OP knows what it does, but wants to know when it would be useful.Donettedoney
In the lost wasteland of my mind, I recall a video of a Caja developer saying that they went to the committee and asked for freeze() in order to implement the security they desired. The dream was that I can have an HTML page that can safely load any third-party JS I want and yet still be sure it can't do anything malicious I don't specifically allow. I don't think things turned out as nicely as they hoped.Mariahmariam
Z
15

You can use this when you have an object representing a logically immutable data structure, especially if:

  • Changing the properties of the object or altering its "duck type" could lead to bad behavior elsewhere in your application
  • The object is similar to a mutable type or otherwise looks mutable, and you want programmers to be warned on attempting to change it rather than obtain undefined behavior.

As an API author, this may be exactly the behavior you want. For example, you may have an internally cached structure that represents a canonical server response that you provide to the user of your API by reference but still use internally for a variety of purposes. Your users can reference this structure, but altering it may result in your API having undefined behavior. In this case, you want an exception to be thrown if your users attempt to modify it.

Zulazulch answered 9/2, 2013 at 20:43 Comment(18)
1. Are you saying that an immediate exception is a better way to go than allowing an object to get modified and be passed around? As to me either way is bad.Infanta
@bonomo As an API author, this may be exactly the behavior you want. For example, you may have an internally cached structure that represents a canonical server response that you provide to the user of your API by reference but still use internally for a variety of purposes. Your users can reference this structure, but altering it may result in your API having undefined behavior. In this case, you want an exception to be thrown if your users attempt to modify it.Zulazulch
2. The programmers won't be warn, rather they will get an exception if the object got frozen and they forgot to do the isFrozen check. Another source of problems at runtime. Let me put it deifferently, you trade problems of one sort for different sort of problens.Infanta
@bonomo You trade a problem with unknown behavior and location to one of known behavior and location, and that's a great trade. The user can see unequivocably that the problem resulted from them attempting to alter a structure your API requires to be immutable. Otherwise, say your API stops responding because your structure's date property was altered, which internally you use for a heartbeat check (for example). Suddenly your API looks unresponsive and buggy, while the user believes he/she is just doing something expedient (maybe just for display purposes, like changing time zone).Zulazulch
What if you got a wrong object frozen or missused this feature in some other wierd way? I mean, it is not a silver bullet, is it? It doesn't automatically guarantee your program from having less problems. In C# immutability can bd enforced at the compile time which is a whole different story.Infanta
What line are you arguing then? If you are stating that this function exists and could be misused - I would agree with you. However I believe Plynx makes a good point in that as a library/public api author this can be a valuable tool if you wish to enforce a behavior in your code. The point in throwing an exception is that you want the developer to see it. If your code is throwing exceptions of any kind, chances are it's not tested very well - or you're doing something wrong (ie, using the library in a manner not intended).Clothier
@bonomo I think you should feel totally free not to use it. As you say, it's not a silver bullet. Immutable data has other general advantages, including the ability for the JIT to make optimizations around the guarantee (though none to my knowledge do so now), and general security, and if you are using a tool like WebStorm that does static analysis it can probably flag modifications to obviously frozen objects for you too before you run the code. If the issue you have is that Javascript is a dynamic language and throws exceptions at runtime... then yes, but isn't that... good?Zulazulch
@plynx, the code optimization thing makes total sense! That could be the reason for making object frozen. Thank you for the sane answer.Infanta
@JonJaques, speaking of APIs, thankfully there are a few legit ways to protect vital properties of an object from being modified using just plain JavaScript without involving immutability. As an author of some public api I would consider them first. So technically you guys are making a valid point, but practically it's not how you do it.Infanta
And the original question was about the practical benefits of this feature. I think that preventing objects from being intentionally/unintentionally modified at a cost of throwing an exception doesn't seem a like a brilliant idea. I believe there must be some other considerations behind introducing this feature. This is what I am trying to figure out.Infanta
@bonomo I think I see how you are thinking of this now. You seem to be thinking of freezing as if it were equivalent to private or const data in class-based languages. But this is not the case. How would you use your other techniques (legit ways) to pass back a reference to an immutable array, for instance?Zulazulch
I am rather thinking equivalents of "c# readonly fields" that guarantee true immutability in c# at compile time. Obviously there can be nothing like that in a dynamic language. As for protecting an array from being modified (as I said without involving immutability) you can consider wrapping it into an accessor like this: jsfiddle.net/JcHw3 It has slightly different syntax than an array, but protects it perfectly.Infanta
@bonomo readonly fields in C# are not guaranteed immutable, only non-assignable. A readonly collection object in C# is still mutable. A frozen javascript array cannot be pushed, reversed, shifted, etc (but can still be sliced, mapped, concatted, etc.). In C# you can pass back a ReadOnlyCollection<T> from a getter for this purpose which works much like your fiddle. With Javascript you can just freeze the object, stopping only mutations and without additional accessor indirection/cost.Zulazulch
By using readonly fields one can create immutable type as a composition of readonly primitive type fields and readonly values of other immutable types. Unfortunately arrays in .Net are not immutable, but they can be substituted by immutable list/tree structures. So in most situation you can create a truly immutable types in C# just by employing readonly fields.Infanta
@bonomo I'm don't program a lot of public use APIs, and in general, I just have public everything instead of trying to lock things down. But I have the benefit of mostly working on only my own code base and not having others work a lot with mine. If I were to want to lock some things down in some future circumstance, I think I would appreciate the flexibility and simplicity Object.freeze offers over, say, complex closure factory patterns or readonly style fields. There are other levels of control too. I understand Harmony will have immutable types.Zulazulch
The problem with Object.freeze is that after freezing it doesn't change its surface. All properties and method are the same. The object looks exactly the same way as it was right before freezing. If you are given 2 similar objects where on is frozen and another one is not you wont be able to tell them apart without runing the isFrozen check or trying to set a property. The approach with accessors that have limited set of methods and only get property accessors conveys different intent that says you cannot use me like you did before, because I am locked now.Infanta
Taking away an invitation for modification can alone stop a developer from trying to modify and object that isn't supposed to be modified. I mean it is quite stupid to try making a pig fly because it just doesn't look like it's capable of doing it. There are no evidences that it has necessary means for it, so why would you try. On the other hand a well crafted stuffed bird can look exactly like a real one, but when you throw it out of the window it will fall down, despite your expectations and beliefs.Infanta
let us continue this discussion in chatZulazulch
S
12

In my nodejs server environment, I use freeze for the same reason I use 'use strict'. If I have an object that I do not want being extended or modified, I will freeze it. If something attempts to extend or modify my frozen object, I WANT my app to throw an error.

To me this relates to consistent, quality, more secure code.

Also, Chrome is showing significant performance increases working with frozen objects.

Edit: In my most recent project, I'm sending/receiving encrypted data between a government entity. There are a lot of configuration values. I'm using frozen object(s) for these values. Modification of these values could have serious, adverse side effects. Additionally, as I linked previously, Chrome is showing performance advantages with frozen objects, I assume nodejs does as well.

For simplicity, an example would be:

var US_COIN_VALUE = {
        QUARTER: 25,
        DIME: 10,
        NICKEL: 5,
        PENNY: 1
    };

return Object.freeze( US_COIN_VALUE );

There is no reason to modify the values in this example. And enjoy the benefits of speed optimizations.

Storytelling answered 9/8, 2015 at 16:21 Comment(2)
I am coming from static type languages, where immutability is enforced at the compile time. I can't really see much value in so called "immutability enforced at runtime" , because violations are inevitable and each violations means a crash. Which is better than nothing, but much less than what you would have gotten with static checking. Hence my question, is there anything more to it other than taking advantage of early crashing at an attempt to modify a so-called immutable object?Infanta
I've edited my answer to clarify 'real life' usage of Object.freeze(). I hope it's helpful. Although you may disagree with it's usage, it suits my purpose very well, and I believe I'm using it for one of the reasons it was intended. Consider accepting it as an answer.Storytelling
C
10

Object.freeze() mainly using in Functional Programming (Immutability)

Immutability is a central concept of functional programming because without it, the data flow in your program is lossy. State history is abandoned, and strange bugs can creep into your software.

In JavaScript, it’s important not to confuse const, with immutability. const creates a variable name binding which can’t be reassigned after creation. const does not create immutable objects. You can’t change the object that the binding refers to, but you can still change the properties of the object, which means that bindings created with const are mutable, not immutable.

Immutable objects can’t be changed at all. You can make a value truly immutable by deep freezing the object. JavaScript has a method that freezes an object one-level deep.

const a = Object.freeze({
 foo: 'Hello',
 bar: 'world',
 baz: '!'
});
Colwell answered 12/9, 2017 at 7:26 Comment(0)
T
5

With the V8 release v7.6 the performance of frozen/sealed arrays is greatly improved. Therefore, one reason you would like to freeze an object is when your code is performance-critical.

Tautog answered 20/6, 2019 at 6:31 Comment(0)
V
4

When you're writing a library/framework in JS and you don't want some developer to break your dynamic language creation by re-assigning "internal" or public properties. This is the most obvious use case for immutability.

Viki answered 23/5, 2016 at 9:1 Comment(1)
It’s a shame because experienced devs know not to modify an object they don’t own…unless there’s a horrible bug or limitation in some library you’re using and carefully tweaking internal state is the only way to avoid long and costly delays. In general I like that mutating internal state is a last resort in much of the JS ecosystemGreatgranduncle
E
3

What is a practical situation when you might want to freeze an object? One example, on application startup you create an object containing app settings. You may pass that configuration object around to various modules of the application. But once that settings object is created you want to know that it won't be changed.

Ezmeralda answered 11/5, 2017 at 13:27 Comment(0)
S
2

The only practical use for Object.freeze is during development. For production code, there is absolutely no benefit for freezing/sealing objects.

Silly Typos

It could help you catch this very common problem during development:

if (myObject.someProp = 5) {
    doSomething();
}

In strict mode, this would throw an error if myObject was frozen.

Enforce Coding Protocol / Restriction

It would also help in enforcing a certain protocol in a team, especially with new members who may not have the same coding style as everyone else.

A lot of Java guys like to add a lot of methods to objects to make JS feel more familiar. Freezing objects would prevent them from doing that.

Shopwindow answered 12/7, 2015 at 16:48 Comment(10)
Why? How does Typescript help answer what Object.freeze is used for?Shopwindow
I totally disagree. What about functional programming? I want to be sure that my pure functions are not modifying anything, even by accident.Esperanto
@Esperanto since you disagree, it means you believe that there is value in freezing objects in production code. In fact, since you totally disagree, it means you believe there is no use in freezing during development, but it is somehow useful in production code. Would you care to share some examples so that we can all be enlightened?Shopwindow
@Shopwindow I believe I described the use case in my previous comment quite clarly. If one want functional programming, the one needs immutable objects. The comments section is not a place to explain the whole paradigm. Oh, and "thank you" for your rude comment...Esperanto
@Esperanto it's a simple request for clarification, I don't see where I have been rude. I just didn't understand why you totally disagree, i.e. you think that it's not useful during development, but only useful for production code. It somehow has to do with functional programming, but I don't see it. In any case, you're right - comments section is not meant for answers. Please do contribute an answer so we can all learn.Shopwindow
I believe this answer is grossly misunderstood. freeze is not proclaimed useless in production, it's just that it should lose any observable effect before going into production. Ideally, I mean, in a world where no bugs get to show up in production. It's more like a part of the workflow. Performance concerns are a different topic though, but these are covered in other answers. I have to admit however, that statically-typed languages achieve the same goals more elegantly (i. e. not at runtime). So I can understand a reference to TypeScript.Had
@Had I agree that using a statically-typed language is a more elegant solution. But the question was why one would use Object.freeze in JavaScript. Like all other answers have alluded to, there is no real benefit in production; it's mainly about code control - not letting others modify your objects in development. If the question was "What can I do to prevent others from modifying objects, without using Object.freeze or seal?" I believe TypeScript would have been a valid answer. As of today, I still don't see how it is useful in production, or totally useless in development.Shopwindow
@Shopwindow Why yes, it's not an answer, it's a comment. Additional information maybe. I was taking a guess at what that could mean.Had
@Had agree. Anyway, comments are supposed to help improve the answer. In this case, I don't see how TypeScript or functional programming help improve the answer. I had hoped the commenters could share some knowledge/experience for everyone's benefit.Shopwindow
Since 19th June 2019 when V8 engine v7.6 released - Object.freeze has a lot of performance value in production. Refer to Philippe's answer. Commenting this here since this is not true. v8.dev/blog/v8-release-76#frozen%2Fsealed-array-improvementsBribery
L
2

This is an old question, but I think I have a good case where freeze might help. I had this problem today.


The problem

class Node {
    constructor() {
        this._children = [];
        this._parent = undefined;
    }

    get children() { return this._children; }
    get parent() { return this._parent; }

    set parent(newParent) {
        // 1. if _parent is not undefined, remove this node from _parent's children
        // 2. set _parent to newParent
        // 3. if newParent is not undefined, add this node to newParent's children
    }

    addChild(node) { node.parent = this; }
    removeChild(node) { node.parent === this && (node.parent = undefined); }
    ...
}

As you can see, when you change the parent, it automatically handles the connection between these nodes, keeping children and parent in sync. However, there is one problem here:

let newNode = new Node();
myNode.children.push(newNode);

Now, myNode has newNode in its children, but newNode does not have myNode as its parent. So you've just broken it.

(OFF-TOPIC) Why are you exposing the children anyway?

Yes, I could just create lots of methods: countChildren(), getChild(index), getChildrenIterator() (which returns a generator), findChildIndex(node), and so on... but is it really a better approach than just returning an array, which provides an interface all javascript programmers already know?

  1. You can access its length to see how many children it has;
  2. You can access the children by their index (i.e. children[i]);
  3. You can iterate over it using for .. of;
  4. And you can use some other nice methods provided by an Array.

Note: returning a copy of the array is out of question! It costs linear time, and any updates to the original array do not propagate to the copy!


The solution

    get children() { return Object.freeze(Object.create(this._children)); }

    // OR, if you deeply care about performance:
    get children() {
        return this._PUBLIC_children === undefined
            ? (this._PUBLIC_children = Object.freeze(Object.create(this._children)))
            : this._PUBLIC_children;
    }

Done!

  1. Object.create: we create an object that inherits from this._children (i.e. has this._children as its __proto__). This alone solves almost the entire problem:
    • It's simple and fast (constant time)
    • You can use anything provided by the Array interface
    • If you modify the returned object, it does not change the original!
  2. Object.freeze: however, the fact that you can modify the returned object BUT the changes do not affect the original array is extremely confusing for the user of the class! So, we just freeze it. If he tries to modify it, an exception is thrown (assuming strict mode) and he knows he can't (and why). It's sad no exception is thrown for myFrozenObject[x] = y if you are not in strict mode, but myFrozenObject is not modified anyway, so it's still not-so-weird.

Of course the programmer could bypass it by accessing __proto__, e.g:

someNode.children.__proto__.push(new Node());

But I like to think that in this case they actually know what they are doing and have a good reason to do so.

IMPORTANT: notice that this doesn't work so well for objects: using hasOwnProperty in the for .. in will always return false.


UPDATE: using Proxy to solve the same problem for objects

Just for completion: if you have an object instead of an Array you can still solve this problem by using Proxy. Actually, this is a generic solution that should work with any kind of element, but I recommend against (if you can avoid it) due to performance issues:

    get myObject() { return Object.freeze(new Proxy(this._myObject, {})); }

This still returns an object that can't be changed, but keeps all the read-only functionality of it. If you really need, you can drop the Object.freeze and implement the required traps (set, deleteProperty, ...) in the Proxy, but that takes extra effort, and that's why the Object.freeze comes in handy with proxies.

Lautrec answered 13/1, 2017 at 3:19 Comment(0)
G
1

I can think of several places that Object.freeze would come in very handy.

The first real world implementation that could use freeze is when developing an application that requires 'state' on the server to match what's in the browser. For instance, imagine you need to add in a level of permissions to your function calls. If you are working in an application there may be places where a Developer could easily change or overwrite the permission settings without even realizing it (especially if the object were being passed through by reference!). But permissions by and large can never change and error'ing when they are changed is preferred. So in this case, the permissions object could be frozen, thereby limiting developer from mistakenly 'setting' permissions erroneously. The same could be said for user-like data like a login name or email address. These things can be mistakenly or maliciously broken with bad code.

Another typical solution would be in a game loop code. There are many settings of game state that you would want to freeze to retain that the state of the game is kept in sync with the server.

Think of Object.freeze as a way to make an object as a Constant. Anytime you would want to have variable constant, you could have an object constant with freeze for similar reasons.

There are also times where you want to pass immutable objects through functions and data passing, and only allow updating the original object with setters. This can be done by cloning and freezing the object for 'getters' and only updating the original with 'setters'.

Are any of these not valid things? It can also be said that frozen objects could be more performant due to the lack of dynamic variables, but I haven't seen any proof of that yet.

Glaucoma answered 18/2, 2015 at 21:55 Comment(0)
B
1

All the other answers pretty much answer the question.

I just wanted to summarise everything here along with an example.

  • Use Object.freeze when you need utmost surety regarding its state in the future. You need to make sure that other developers or users of your code do not change internal/public properties. Alexander Mills's answer
  • Object.freeze has better performance since 19th June, 2019, ever since V8 v7.6 released. Philippe's answer. Also take a look at the V8 docs.

Here is what Object.freeze does, and it should clear out doubts for people who only have surface level understanding of Object.freeze.

const obj = {
  name: "Fanoflix"
};

const mutateObject = (testObj) => {
  testObj.name = 'Arthas' // NOT Allowed if parameter is frozen
}

obj.name = "Lich King" // Allowed
obj.age = 29; // Allowed
mutateObject(obj) // Allowed
 
Object.freeze(obj) // ==========  Freezing obj ========== 

mutateObject(obj) // passed by reference NOT Allowed
obj.name = "Illidan" // mutation NOT Allowed
obj.age = 25; // addition NOT Allowed
delete obj.name // deletion NOT Allowed

Bribery answered 18/1, 2023 at 11:18 Comment(0)
D
0

I could see this being useful when you're working with an interactive tool. Rather than:

if ( ! obj.isFrozen() ) {
    obj.x = mouse[0];
    obj.y = mouse[1];
}

You could simply do:

obj.x = mouse[0];
obj.y = mouse[1];

Properties will only update if the object isn't frozen.

Donettedoney answered 9/2, 2013 at 20:43 Comment(2)
This would throw TypeErrorZulazulch
This aproach doesn't do any good to readability of the code. I think it is a bad idea to use it this way. Moreover somd implementations trhow an exception on an attempt to modifya frozen object.Infanta
B
0

Don't know if this helps, but I use it to create simple enumerations. It allows me to hopefully not get duff data in a database, by knowing the source of the data has been attempted to be unchangeable without purposefully trying to break the code. From a statically typed perspective, it allows for reasoning over code construction.

Batavia answered 22/2, 2017 at 11:8 Comment(0)
G
0

In a library things can break easily if people modify core things not intended to be modified, here an example in a 3d library:

export const O  =new Vec3(0,0,0)
export const Z  =new Vec3(0,0,1)
export const Y  =new Vec3(0,1,0)
export const YZ =new Vec3(0,1,1)
export const X  =new Vec3(1,0,0)
export const XY =new Vec3(1,1,0)
export const ZX =new Vec3(1,0,1)
export const XYZ=new Vec3(1,1,1)

const N1=X.cross(Y) // this operation would add Y in X
console.log(Y) //=> Vec3(0,1,0)
console.log(X) //=> Vec3(1,1,0)
const N2=Vec3.add(X,Y) // this operation would add X and Y but on a new vector
console.log(N2) //=> Vec3(1,2,0)

Any of those vector are set as const but it's actually possible to alter their properties, by volontary or involontary means, also those vectors are reused everywhere in this type of libraries, freezing them is a way to prevent such involontary modifications or mainly to throw if you try to modify them and so the library can stay consistent.

Greasy answered 13/2 at 23:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.