Create immutable object in javascript - javascript

I have a routine where I receive some data from an api. I'd like to store this data in an object, but after that i want to "lock" this object and not allow any change to the properties or their values after that point. Is that possible? (If possible using only ES5).

If you wish for an object to not be able to be modified you can use Object.freeze.
The Object.freeze() method freezes an object: that is, prevents new
properties from being added to it; prevents existing properties from
being removed; and prevents existing properties, or their
enumerability, configurability, or writability, from being changed, it
also prevents the prototype from being changed. The method returns
the object in a frozen state.
If you simply want to prevent a variable from being reassigned you can use const (ES6), however note that:
The const declaration creates a read-only reference to a value. It
does not mean the value it holds is immutable, just that the variable
identifier cannot be reassigned.
E.g, the following is perfectly valid
const a = { x: 7 }
a.x = 9
console.log(a.x) // 9
However, trying to reassign a variable declared with const will throw a TypeError:
const a = 5
a = 7

For Immutable object we can use below approches
Object.freeze()
To enforce object immutability while update the object make sure to
use Object.assign({},a,{foo:'bar'}) rather than a.foo='bar'
we can use spread(...) operator.
See example:
var person={name:'pavan',age:26}
var newPerson={...person,name:'raju'}
console.log(newPerson ===person) //false
console.log(person) //{name:'pavan',age:26}
console.log(newPerson) //{name:'raju',age:26}

You can use Object.freeze() method which freezes an object. A frozen object can no longer be changed and prevents :
New properties from being added to it
Existing properties from being removed
Changing the enumerability, configurability, or writability of existing properties
The values of existing properties from being changed.
Demo :
const obj = {
name: 'Alpha',
age: 30,
hobbies: ['reading', 'writing']
};
Object.freeze(obj);
obj.name = 'Beta';
obj.hobbies[0] = 'painting';
console.log(obj.name); // Alpha (It works fine with the properties which contains primitive values)
console.log(obj.hobbies[0]); // painting (It does not work for nested objects/array)
If you see in above code snippet, It works fine with the properties which contains primitive values but it is updating the values of nested object/array.
Hence, You can try as const If you are working with typescript in your requirement. It will freeze the whole object along with the nested properties with object/array.

No. There's no good way to enforce const-ness is ES5.

If you separate you code into pure and impure parts (as with functional core, imperative shell), you can get by in the pure parts using objects and arrays and not, as a matter of discipline, mutating them.
I've done this for years and have a library which aids this style of development. I can't imagine needing the insurance policy of having to Object.freeze everything. Now, eventually, when records and tuples is adopted into the spec you can just use them instead!

Related

What is the purpose of comparing two objects? (as opposed to primitives)

I'm currently learning Javascript (I'm familiar with C# and Python), and currently the tutorial I'm reading is discussing comparison between two objects. In the coding I've done (I've had various projects), this sort of thing has never really been needed. Given that this might be important for the future, I thought I'd look for when/where to use object comparison, but all I can find are questions/answers on how it works, not why you should use it and when. I can't think of any situations off the top of my head where comparisons between primitives wouldn't be better, so any help in this area would be appreciated.
You would use it when you have two variables which refer to objects, and you want to see whether they refer to the same object. For example, with the window object, you can do
if (window !== window.top) {
console.log('This script must be running in an iframe!');
}
I can't think of any situations off the top of my head where comparisons between primitives wouldn't be better
Many objects (including window) can't just be converted to a unique primitive easily.
You may be able to use JSON.stringify to convert a plain object to a string, and then compare those strings, but this won't help you compare whether the objects are actually the same object in memory:
const obj1 = { prop: 'val' };
const obj2 = { prop: 'val' };
// They are not the same object
console.log(obj1 === obj2);
// But if you convert them to a primitive string first, you won't be able to tell
console.log(JSON.stringify(obj1) === JSON.stringify(obj2));
At the risk of a circular argument, you use it when you need to use it. :-)
I'd say it's roughly the same as in C#. In both cases, when you use an equality operator, it tests to see whether they're the same object (rather than separate but equivalent objects).
For instance, suppose you have an array of objects, and the contents of that array can change. Later, you have an object you should remove. You'd compare the object to remove with the objects in the array to see if you should remove it (often in a filter operation):
arrayOfObjects = arrayOfObjects.filter(obj => obj !== objToRemove);

Object unexpectedly being modified after push into array

I have what seems like it should be a simple operation. For each bridgedSection, I check for a potentialSection with an id that matches the bridged.referenceSection
Then I take that result, parse the HTML on the object with Cherio, make a slight modification (using an id for testing), and then store both the bridgedSection and the modified result on an object, then push that object to the array.
If I log the new object BEFORE pushing, I get the correct object values. If I log it from the array I get incorrect values only for reference.section. bridgedSection is fine, but reference.section matches across all entries in the array.
To say that I'm thoroughly flummoxed is an understatement. Can anyone shed some light on what I am (clearly) doing wrong?
var sectionCount = 0;
bridgedSections.forEach(bridged => {
var obj = potentialSections.find(obj => obj._id == bridged.referenceSection);
$ = cheerio.load(obj.html);
$(".meditor").html(bridged._id);// dropping the id here so it's easy to see if it was updated
obj.html = $.html();
obj.rand = Math.floor(Math.random() * 1000); // can't seem to add to obj either
var thisSection = {
referenceSection: obj,
bridgedSection: bridged,
}
console.log(thisSection) // correct value logged
currentSections.push(thisSection);
sectionCount++;
});
console.log(currentSections);
// this logs an array of the correct length but each
// {}.referenceSection is identical to the last entry pushed above
To try to clarify what both of the above folks are saying, the JavaScript language (like many others) has the concept of references, and makes very heavy use of that concept.
When one variable "refers to" another, there is only one copy of the value in question: everything else is a reference to that one value. Changes made to any of those references will therefore change the [one ...] underlying value (and, be reflected instantaneously in all of the references).
The advantage of references is, of course, that they are extremely "lightweight."
If you need to make a so-called "deep copy" of an array or structure or what-have-you, you can do so. If you want to push the value and be sure that it cannot be changed, you need to make sure that what you've pushed is either such a "deep copy," or that there are no references (as there obviously are, now ...) to whatever it contains. Your choice.
N.B. References – especially circular references – also have important implications for memory management (and "leaks"), because a thing will not be "reaped" by the memory manager until all references to it have ceased to exist. (Everything is "reference counted.")
And, all of what I've just said pretty much applies equally to every language that supports this – as most languages now do.
Javascript is passes function parameters by reference. This means the following happens:
derp = {a:1}
function passedByRef(param){
param['a'] = 2;
}
passedByRef(derp)
console.log(derp['a']) // 2
So when you pass a json object to a function, if you modify said object in the function it will change the original object. You probably want to make a deep copy of bridged before you assign it to thisSection because if you modify the version of bridged later on in thisSection it will modify the original object.
Here is a post that talks about cloning objects or you could look into something like immutable js
I think you need to look into Javascript deep copy.
You are modifying the original object when you modify the second assigned variable, because they are pointing to the same object. What you really need is to duplicate the object, not simply making a pointer to it.
Take a look at this:
https://scotch.io/bar-talk/copying-objects-in-javascript#toc-deep-copying-objects

Is the use of Objects as keys inefficient in Javascript?

Let's say I define a couple of classes. Then I create object of each class. I want to save the objects in a map keyed by their classes, so that I can retrieve the object based on its class.
(I'm using ES6 syntax, however the question may remain the same for legacy Javascript with class replaced by function)
// Alternative 1
class Apple {
}
class Banana {
}
let fruitInBag = {};
fruitInBag[Apple] = new Apple();
fruitInBag[Banana] = new Banana();
Alternatively I could also write following with the same outcome
// Alternative 2
class Apple {
}
class Banana {
}
const FRUIT_TYPE_APPLE = 1;
const FRUIT_TYPE_BANANA = 2;
let fruitInBag = {};
fruitInBag[FRUIT_TYPE_APPLE] = new Apple();
fruitInBag[FRUIT_TYPE_BANANA] = new Banana();
The second alternative is awkward, because I've to define and maintain the constants separate from class definitions. Therefore I would prefer the first one. But is the first approach inefficient? Is the Object smart enough to implement the first alternative efficiently?
The Keys in a Javascript object are always strings. No Integers and no functions (Classes). Use a ES6 Map for this!
I updated the code snippet in the question to correctly reflect the problem. After having done that and taking the suggestions from the comments and deleted answer, I did some analysis in Chrome devtools as follows.
As you can see from the picture, I defined a class in both ES6 (class Apple) and ES5 (function FnApple) manner. Then I kept them in regular ES5 object (mapA) and later ES6 map (mapB). In all the cases the browser stringifies the class definition completely and uses that string as a key. This works, because complete stringification of class makes it distinct. (I was afraid the stringification will the a dump [Object] string).
However this also means that using the class or function object as key will make the map key of indeterminately long size. In real world scenario when classes have long definitions, the key can go upto few KBs. Given this, it makes sense that one should use independently defined integers for keys in maps.
Update
In case of ES6 map, my test above is incorrect. I assigned the key-value to the mapB object using [] syntax, which makes it store the key-value as member of object like ES5 (because even ES6 Map instance is an object). The right way to set key-value on ES6 map is to use get(),set() API. After doing that I see that the type of keys is indeed function and not String for ES6 Map.

Maps vs Objects in ES6, When to use?

Ref: MDN Maps
Use maps over objects when keys are unknown until run time, and when
all keys are the same type and all values are the same type.
Use objects when there is logic that operates on individual elements.
Question:
What is an applicable example of using Maps over objects? in particular, "when would keys be unknown until runtime?"
var myMap = new Map();
var keyObj = {},
keyFunc = function () { return 'hey'},
keyString = "a string";
// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");
console.log(myMap.get(keyFunc));
What is an applicable example of using Maps over objects?
I think you've given one good example already: You at least need to use Maps when you are using objects (including Function objects) as keys.
in particular, "when would keys be unknown until runtime?"
Whenever they are not known at compile time. In short, you should always use a Map when you need a key-value collection. A good indicator that you need a collection is when you add and remove values dynamically from the collection, and especially when you don't know those values beforehand (e.g. they're read from a database, input by the user, etc).
In contrast, you should be using objects when you know which and how many properties the object has while writing the code - when their shape is static. As #Felix has put it: when you need a record. A good indicator for needing that is when the fields have different types, and when you never need to use bracket notation (or expect a limited set of property names in it).
I think that with ES2015's Map only two reasons are left to use plain objects:
You don't want to iterate over the properties of an object type at all
or you do but the property order doesn't matter and you can distinguish the program from the data level when iterating
When is the property order unimportant?
if you have only a single value and some functions that should be associated explicitly with it (like Promise - which is a proxy for a future value - and then/catch)
if you have a struct/record-like data structure with a static set of properties known at "compile time" (usually structs/records aren't iterable)
In all other cases you might consider using Map, because it preserves property order and separates the program (all properties assigned to the Map object) from the data level (all entries in the Map itself).
What are the drawbacks of Map?
you lose the concise object literal syntax
you need custom replacers for JSON.stringyfy
you lose destructuring, which is more useful with static data structures anyway
Use maps over objects when keys are unknown until run time, and when all keys are the same type and all values are the same type.
I have no idea why someone would write something so obviously wrong. I have to say, people are finding more and more wrong and/or questionable content on MDN these days.
Nothing in that sentence is correct. The main reason to use maps is when you want object-valued keys. The idea that the values should be the same type is absurd--although they might be, of course. The idea that one shouldn't use objects when keys are unknown until run time is equally absurd.
One of the difference between Map and Object is:
Map can use complex data type as its key. like this:
const fn = function() {}
const m = new Map([[document.body, 'stackoverflow'], [fn, 'redis']]);
m.get(document.body) // 'stackoverflow'
m.get(fn) //'redis'
watch out: For complex data type, If you want to get the value, you must pass the same reference as the key.
Object, it only accept simple data type(number, string) as its key.
const a = {};
a[document.body] = 'stackoverflow';
console.log(a) //{[object HTMLBodyElement]: "stackoverflow"}
Objects are similar to Maps in that both let you set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Because of this (and because there were no built-in alternatives), Objects have been used as Maps historically; however, there are important differences that make using a Map preferable in certain cases:
The keys of an Object are Strings and Symbols, whereas they can be
any value for a Map, including functions, objects, and any primitive.
The keys in Map are ordered while keys added to object are not. Thus,
when iterating over it, a Map object returns keys in order of
insertion.
You can get the size of a Map easily with the size property, while
the number of properties in an Object must be determined manually.
A Map is an iterable and can thus be directly iterated, whereas
iterating over an Object requires obtaining its keys in some fashion
and iterating over them.
An Object has a prototype, so there are default keys in the map that
could collide with your keys if you're not careful. As of ES5 this
can be bypassed by using map = Object.create(null), but this is
seldom done.
A Map may perform better in scenarios involving frequent addition and
removal of key pairs.
MDN
This question is a duplicate of but until it's closed, here's my answer from over there:
In addition to the other answers, I've found that Maps are more unwieldy and verbose to operate with than objects.
obj[key] += x
// vs.
map.set(map.get(key) + x)
This is important, because shorter code is faster to read, more directly expressive, and better kept in the programmer's head.
Another aspect: because set() returns the map, not the value, it's impossible to chain assignments.
foo = obj[key] = x; // Does what you expect
foo = map.set(key, x) // foo !== x; foo === map
Debugging maps is also more painful. Below, you can't actually see what keys are in the map. You'd have to write code to do that.
Objects can be evaluated by any IDE:

When should I prefer a clone over an reference in javascript?

at the moment I'm writing a small app and came to the point, where I thought it would be clever to clone an object, instead of using a reference.
The reason I'm doing this is, because I'm collecting objects in a list. Later I will only work with this list, because it's part of a model. The reference isn't something I need and I want to avoid having references to outside objects in the list, because I don't want someone to build a construct, where the model can be changed from an inconsiderate place in their code. (The integrity of the information in the model is very important.)
Additional I thought I will get a better performance out of it, when I don't use references.
So my overall question still is: When should I prefer a clone over an reference in javascript?
Thanks!
If stability is important, then clone it. If testing shows that this is a bottleneck, consider changing it to a reference. I'd be very surprised if it is a bottleneck though, unless you have a very complicated object which is passed back and forth very frequently (and if you're doing that it's probably an indication of a bad design).
Also remember that you can only do so much to save other developers from their own stupidity. If they really want to break your API, they could just replace your functions with their own by copying the source or modifying it at runtime. If you document that the object must not be changed, a good developer (yes, there are some) will follow that rule.
For what it's worth, I've used both approaches in my own projects. For small structs which don't get passed around much, I've made copies for stability, and for larger data (e.g. 3D vertex data which may be passed around every frame), I don't copy.
Why not just make the objects stored in the list immutable? Instead of storing simple JSON-like objects you would store closures.
Say you have an object with two properties A and B. It looks like that:
myObj = {
"A" : "someValue",
"B" : "someOtherValue"
}
But then, as you said, anyone could alter the state of this object by simply overriding it's properties A or B. Instead of passing such objects in a list to the client, you could pass read-only data created from your actual objects.
First define a function that takes an ordinary object and returns a set of accessors to it:
var readOnlyObj = function(builder) {
return {
getA : function() { return builder.A; },
getB : function() { return builder.B; }
}
}
Then instead of your object myObj give the user readOnlyObj(myObj) so that they can access the properties by methods getA and getB.
This way you avoid the costs of cloning and provide a clear set of valid actions that a user can perform on your objects.

Categories