what does freezing objects do to their properties

When working with values and objects in JavaScript, y'all may sometimes need to restrict what tin be done with them to foreclose changes to awarding-wide configuration objects, land objects, or global constants.

Functions with access to such data may modify them directly when they should not (and this can also stalk from unintentional mistakes made by developers). Additionally, other developers working on the same codebase (or using your code) may make such changes unexpectedly.

JavaScript thankfully provides a few constructs to handle these kinds of situations.

In this tutorial, we'll discuss the concept of immutability and the freeze() and seal() object methods in JavaScript. We'll run into how they work using illustrative lawmaking samples and discuss possible functioning limitations. At present, let's become to it!

Agreement immutability in JavaScript

In brief, making an object immutable means that further changes to it will not apply. Essentially, its state becomes read-simply. This is, to an extent, what the const keyword achieves:

const jarOfWine = "full";  // throws fault "Uncaught TypeError: Assignment to constant variable." jarOfWine = "empty";        

But of form, we can't utilize const for entities such as objects and arrays because of how const declarations work — it simply creates a reference to a value. To explain this, permit's review the JavaScript data types.

Primitives vs. objects

The first set of information types are values that consist of just one particular. These include primitives such every bit strings or numbers that are immutable:

let nextGame = "Word Duel";  // change to "Give-and-take Dual"? Doesn't stick. nextGame[vii] = "a";  nextGame; // still "Word Duel"  // Of course, if we'd declared nextGame with `const`, then we couldn't reassign it. nextGame = "Word Dual";  nextGame; // now "Word Dual"        

When we copy these primitive types, nosotros're copying values:

const jarOfWine = "full";  const emptyJar = jarOfWine; // both jars are now 'full'        

Both variables, jarOfWine and emptyJar, now contain ii separate strings, and y'all can alter any one of them independently of the other. However, objects conduct differently.

When yous declare an object, similar in the following code, the user variable does not contain the object itself, but a reference to it:

const user = {   name: "Jane",   surname: "Traveller",   stayDuration: "3 weeks",   roomAssigned: 1022, }        

Information technology's like writing down the address to the cave containing your pile of gilded. The address isn't the cave. So, when we endeavour to copy an object using the same method of assignment as when we copied strings, we end up copying simply the reference or address and we don't have 2 separate objects:

const guest = user;        

Modifying user too changes guest:

guest.proper name = "John";  // at present both user and guest wait similar this: {   name: "John",   surname: "Traveller",   stayDuration: "3 weeks",   roomAssigned: 1022, }        

You can usually test this with the Object.is() method or the strict equality operator:

Object.is(user, guest) // returns truthful  user === guest // returns true        

It's a like play with the const keyword. It creates a reference to a value, pregnant that although the binding cannot alter (that is, y'all cannot reassign the variable), the value referenced can change.

This occured when we successfully modified the proper name property earlier, fifty-fifty though invitee was declared with const:
<

guest.proper name = "John";        

In other words, what const gives us is assignment immutability, not value immutability.

Restricting changes to object properties and entire objects

Since objects in JavaScript are copied by reference, there's always the risk that copied references mutate the original object. Depending on your use instance, such behavior may not be desirable. In that instance, it may make sense to essentially "lock down" the object.

(Ideally, you'd make copies of your object and modify those, rather than the original object. While most copying or cloning mechanisms are shallow, if y'all're working with deeply nested objects, then you'd want deep cloning.)

JavaScript provides three methods that perform varying levels of admission restriction to objects. These include Object.freeze(), Object.seal(), and Object.preventExtensions(). Although we'll cover the latter somewhat, we'll focus mostly on the former 2.

writable and configurable belongings flags

Earlier we movement on, however, allow's walk through some underlying concepts behind the mechanisms that limit access to properties. Specifically, we're interested in property flags, such every bit writable and configurable.

Y'all tin can typically bank check the values of these flags when using the Object.getOwnPropertyDescriptor or Object.getOwnPropertyDescriptors methods:

const hunanProvince = {   typeOfWine: "Emperor's Grin", };  Object.getOwnPropertyDescriptors(hunanProvince);  // returns {   typeOfWine: {     value: "Emperor's Smiling",     writable: true,     enumerable: true,     configurable: truthful   }, }        

Although we're usually more concerned with the bodily values of our properties when we piece of work with JavaScript objects, properties take other attributes in addition to the value aspect, which holds the value of the property.

These include the already mentioned value, writable, and configurable attributes, as well equally enumerable, as seen above.

The writable and configurable flags are the most of import to the states. When writable is set to true for a property, its value tin can change. Otherwise, it's read-only.

So there's configurable, which, when set to true on a property, lets y'all make changes to the aforementioned flags or delete a property.

If configurable is instead set up to false, everything essentially becomes read-simply with ane exception: if writable is set to true where configurable is false, the value of the property tin can even so modify:

Object.defineProperty(hunanProvince, "capital", {   value: "Caiyi Town",   writable: true, });  hunanProvince.capital = "Possibly Gusu";  Object.getOwnPropertyDescriptors(hunanProvince); // now returns {   typeOfWine: {     value: "Emperor's Smile",     writable: true,     enumerable: true,     configurable: true   },   capital: {     value: "Perhaps Gusu",     writable: true,     enumerable :false,     configurable: fake   }, }        

Note that enumerable and configurable are both fake for the majuscule property hither considering it was created with Object.defineProperty(). Every bit mentioned earlier, backdrop created this way have all flags set to fake. However writable is true because nosotros set that explicitly.

We're also immune to alter writable from truthful to faux, merely that's information technology. You lot can't alter information technology from false to true. In fact, one time both configurable and writable are set to false for a property, no further changes to information technology are allowed:

Object.defineProperty(hunanProvince, "uppercase", {   writable: false,   // everything else also `false` });  // no event hunanProvince.capital = "Caiyi Town";        

While these flags are used hither on a property level, methods like Object.freeze() and Object.seal() work on an object level. Let'southward movement on to that now.

This article assumes you have a general knowledge of why the concept of immutability is useful.

Notwithstanding, if you'd like to dig deeper and read some arguments for and against information technology, hither's a really handy StackOverflow thread (with links to additional resources) that discusses the topic. The Immutable.js docs also brand a instance for immutability.

Using Object.freeze vs. Object.seal for object immutability

Now, permit's accept a look at the freeze and seal methods.

Using Object.freeze

When nosotros freeze an object using Object.freeze, it tin can no longer be modified. Essentially, new backdrop can no longer exist added to it and existing backdrop cannot be removed. Every bit you can approximate, this is achieved by setting all flags to false for all properties.

Let's walk through an case. Here are the two objects we'll piece of work with:

let obj1 = {   "ane": 1,   "2": 2, };  permit obj2 = {   "three": 3,   "four": 4, };        

Now, let's change a property in the first object, obj1:

obj1.1 = "one"; // returns "ane"        

Then, the original object now looks like this:

obj1;  {   one: "1",   two: ii, };        

Of grade, this is expected behavior. Objects are alterable by default. Now, permit'southward try freezing an object. We'll work with obj2 since it hasn't been tampered with yet:

// freeze() returns the same object passed to it Object.freeze(obj2); // returns {three: 3, four: 2}  // exam obj2 === Object.freeze(obj2); // returns true        

To test that an object is frozen, JavaScript provides the Object.isFrozen() method:

Object.isFrozen(obj2); // returns truthful        

Now, even if nosotros attempted to alter it like the following, there is no effect.

obj2.3 = "3"; // no effect        

Still, every bit nosotros'll see before long, we'll come across trouble when we start using nested objects. Similar object cloning, freezing can also exist shallow or deep.

Let'southward create a new object from obj1 and obj2 and nest an assortment in information technology:

// nesting let obj3 = Object.assign({}, obj1, obj2, {"otherNumbers": {   "even": [half-dozen, eight, 10],   "odd": [v, vii, nine], }});  obj3; // { //    i: "one", //    ii: 2, //    3: 3, //    four: iv, //    "otherNumbers": { //      "even": [half-dozen, 8, 10], //      "odd": [v, 7, nine], //    } //  }        

You'll discover that even when we freeze it, nosotros can notwithstanding make changes to the arrays in the nested object:

Object.freeze(obj3);  obj3.otherNumbers.even[0] = 12;  obj3; // { //    one: "1", //    ii: two, //    three: 3, //    iv: iv, //    "otherNumbers": { //      "even": [12, 8, 10], //      "odd": [five, 7, 9], //    } //  }        

The even number array now has its first chemical element modified from 6 to 12. Since arrays are besides objects, this behavior comes up hither as well:

allow testArr = [0, ane, ii, three, [4, 5, [vi, 7]]];  Object.freeze(testArr);  testArr[0] = "nothing"; // unable to modify peak-level elements...  // ...notwithstanding, nested elements can be changed  testArr[4][0] = "four"; // at present looks similar this: [0, ane, two, 3, ["iv", 5, [6, 7]]]        

If you've been testing out your code in the browser console, it likely failed silently and didn't throw any errors. If you'd similar the errors to be more explicit, try wrapping your lawmaking in an Immediately Invoked Part Expression (IIFE) and turn on strict mode:

(function() {   "employ strict";    let obj = {"i": 1, "two": two};    Object.freeze(obj);    obj.ane = "one"; })();        

The to a higher place code should now throw a TypeError in the console:

Uncaught TypeError: Cannot assign to read only property 'one' of object '#<Object>'        

Now, how do we make our entire object, including pinnacle-level (straight property references) and nested properties, frozen?

As we've noted, freezing is only applied to the pinnacle-level properties in objects, and so a deepFreeze() function that freezes each property recursively is what we want:

const deepFreeze = (obj) => {   // fetch belongings keys   const propKeys = Object.getOwnPropertyNames(obj);    // recursively freeze all properties   propKeys.forEach((primal) => {     const propValue = obj[cardinal];      if (propValue && typeof(propValue) === "object") deepFreeze(propValue);   });    return Object.freeze(obj); }        

Now, attempts to mutate the nested properties are unsuccessful.

Note that while freezing essentially guards against changes to objects, it does allow variable reassignment.

Using Object.seal()

With Object.freeze(), new changes take no issue on the frozen object. However, the seal() method allows modifying existing properties. This means that while you lot cannot add new backdrop or remove existing ones, y'all can make changes.

The seal() method basically sets the configurable flag we discussed earlier to false, with writable set to true for each property:

const students = {   "001" : "Kylie Yaeger",   "002": "Ifeoma Kurosaki" };  // seal object Object.seal(students);  // test Object.isSealed(students); // returns true  // cannot add or delete properties students["003"] = "Amara Male monarch"; // fails delete students["001"]; // fails        

Here'south another example with an array:

const students = ["Kylie Yaeger", "Ifeoma Kurosaki"];  // seal Object.seal(students);  // test Object.isSealed(students); // returns true  // throws a TypeError maxim object is not extensible students.push("Amara Male monarch");        

Sealing besides prevents redefining a holding with the apply of Object.defineProperty() or Object.defineProperties(), whether you're calculation a new belongings or modifying an existing 1.

Recall, however, that if writable is true, y'all may still modify it to fake, but this cannot be undone.

// fails Object.defineProperty(hunanProvince, "majuscule", {   value: "Unknown",   writable: true, });        

Another alter sealing makes impossible is changing normal data properties into accessors (that is, getters and setters):

// fails Object.defineProperty(hunanProvince, "capital", {   get: () => "Caiyi Town",   set: (val) => hunanProvince["capital"] = val; });        

The reverse is also the case: you cannot alter accessors into data properties. Just as with freezing, sealing an object prevents its paradigm from changing:

const languageSymbols = {   English language: "ENG",   Japanese: "JP",   French: "FR", };  const trollLanguageSymbols = {   trollEnglish: "T-ENG",   trollJapanese: "T-JP",   trollFrench: "T-FR", };  Object.seal(trollLanguageSymbols);  // fails Object.setPrototypeOf(trollLanguageSymbols, languageSymbols);        

Again, just as with freezing, the default beliefs hither is shallow sealing. So, you lot can choose to deep-seal an object in the same way equally you can deep-freeze one:

const deepSeal = (obj) => {   // fetch property keys   const propKeys = Object.getOwnPropertyNames(obj);    // recursively seal all properties   propKeys.forEach((key) => {     const propValue = obj[key];      if (propValue && typeof(propValue) === "object") deepSeal(propValue);   });    return Object.seal(obj); }        

We've modified MDN'due south deepFreeze() function here to perform sealing instead:

const students = {   "001" : "Kylie Yaeger",   "002": "Ifeoma Kurosaki",   "003": {     "004": "Yumi Ren",     "005": "Plisetsky Ran",   }, };  deepSeal(students);  // fails delete students["003"]["004"];        

Now, our nested objects are also sealed.

Using Object.preventExtensions()

Some other JavaScript method that tin can specifically foreclose adding new properties is the preventExtensions() method:

(() => {   "use strict";    const trollToken = {     name: "Troll",     symbol: "TRL",     decimal: vi,     totalSupply: 100_000_000,   };    Object.preventExtensions(trollToken);    // fails   trollToken.transfer = (_to, corporeality) => {} })();        

Since all we're doing is preventing adding new properties, existing ones can patently be modified and even deleted:

delete trollToken.decimal;  trollToken;  // { //    name: "Troll", //    symbol: "TRL", //    totalSupply: 100_000_000, //  }        

Something to note is that the [[prototype]] holding becomes immutable:

const token = {   transfer: () => {},   transferFrom: () => {},   approve: () => {}, };  // fails with a TypeError Object.setPrototypeOf(trollToken, token);        

To test whether an object is extensible, simply use the isExtensible() method:

// I've omitted `console.log` here since I'm assuming you're typing in the browser panel directly (`Is trollToken extensible? Ans: ${Object.isExtensible(trollToken)}`);        

Just similar when we manually ready the configurable and writable flags to faux for a property, making an object inextensible is a ane-way route.

Object.freeze and Object.seal use cases and performance concerns

To summarize, Object.freeze() and Object.seal()are constructs provided by the JavaScript language to help maintain varying levels of integrity for objects. Still, information technology can be quite disruptive to understand when ane would need to use these methods.

1 example mentioned earlier is the utilize of global objects for awarding state management. You may want to keep the original object immutable and brand changes to copies, especially if you'd similar to go along track of state changes and revert them.

Freezing defends against code attempting to mutate objects that should not be modified directly.

Frozen or sealed objects can likewise prevent the addition of new properties that are introduced due to typos, such every bit mistyped belongings names.

These methods help when debugging as well because the restrictions placed on objects can help narrow downward possible sources of bugs.

That said, it tin be a source of headache for anyone using your code since there is essentially no physical difference between a frozen object and a not-frozen one.

The only style to know for certain that an object is frozen or sealed is to apply the isFrozen() or isSealed() methods. This tin get in somewhat difficult to reason well-nigh expected object beliefs because it may not be entirely obvious why such restrictions were put in place.

Tagged templates are one characteristic that use Object.freeze() implicitly; the styled-components library and a few others rely on it. The onetime uses tagged template literals to create its styled components.

If you're wondering what — if any — performance costs exist when using any of the to a higher place-discussed methods, at that place were some historical performance concerns in the V8 engine. However, this was more a bug than anything else, and it's since been fixed.

Between 2013 and 2014, both Object.freeze() and Object.seal() besides underwent some performance improvements in V8.

Here'due south a StackOverflow thread that tracked the functioning of frozen objects vs. non-frozen objects between 2015 and 2019. It shows that performance in both cases is pretty much the same in Chrome.

Still, it'southward possible sealing or freezing may bear on an object's enumeration speed in certain browsers like Safari.

Third-party libraries for handling immutability

There are multiple means to handle immutability in JavaScript. While the methods discussed above can be handy to have around, y'all'll well-nigh likely reach for a library for whatever substantial application.

Examples include Immer and Immutable.js. With Immer, you use the same JavaScript data types you lot already know. Still, although Immutable.js introduces new data structures, it can be the faster option.

Conclusion

JavaScript provides methods such equally Object.freeze() and Object.seal() for varying levels of access restriction for objects.

However, just equally with cloning, because objects are copied by reference, freezing is usually shallow. Therefore, you tin either implement your own basic deep freeze or deep seal functions or, depending on your apply case, accept advantage of libraries such as Immer or Immutable.js.

LogRocket: Debug JavaScript errors more easily by understanding the context

Debugging code is always a tedious task. Just the more than y'all empathise your errors the easier it is to fix them.

LogRocket allows you to empathise these errors in new and unique means. Our frontend monitoring solution tracks user appointment with your JavaScript frontends to requite you the ability to find out exactly what the user did that led to an error.

LogRocket Dashboard Free Trial Banner

LogRocket records panel logs, page load times, stacktraces, tedious network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

Try information technology for costless.

gowinguly1984.blogspot.com

Source: https://blog.logrocket.com/javascript-object-immutability-object-freeze-vs-object-seal/

0 Response to "what does freezing objects do to their properties"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel