Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to freeze an ES6 Map?

I'm looking for a way to freeze native ES6 Maps.

Object.freeze and Object.seal don't seem to work:

let myMap = new Map([["key1", "value1"]]); // Map { 'key1' => 'value1' }  Object.freeze(myMap); Object.seal(myMap);  myMap.set("key2", "value2"); // Map { 'key1' => 'value1', 'key2' => 'value2' } 

Is this intended behavior since freeze freezes properties of objects and maps are no objects or might this be a bug / not implemented yet?

And yes I know, I should probably use Immutable.js, but is there any way to do this with native ES6 Maps?

like image 349
Tieme Avatar asked Mar 02 '16 12:03

Tieme


People also ask

How do you freeze Javascript?

Object.freeze() Method freeze() which is used to freeze an object. Freezing an object does not allow new properties to be added to an object and prevents from removing or altering the existing properties. Object. freeze() preserves the enumerability, configurability, writability and the prototype of the object.

How do you freeze an array in Javascript?

As an object, an array can be frozen; after doing so, its elements cannot be altered and no elements can be added to or removed from the array. freeze() returns the same object that was passed into the function. It does not create a frozen copy.

Is object freeze recursive?

Mainly, the idea is to freeze the object itself and then recursively freeze each of its properties. We must ensure that we only freeze the object's own properties; we shouldn't mess with the prototype of the object! Note that, in the same way as Object. freeze() works, deepFreeze() also freezes the object in place.


2 Answers

There is not, you could write a wrapper to do that. Object.freeze locks an object's properties, but while Map instances are objects, the values they store are not properties, so freezing has no effect on them, just like any other class that has internal state hidden away.

In a real ES6 environment where extending builtins is supported (not Babel), you could do this:

class FreezableMap extends Map {     set(...args){         if (Object.isFrozen(this)) return this;          return super.set(...args);     }     delete(...args){         if (Object.isFrozen(this)) return false;          return super.delete(...args);     }     clear(){         if (Object.isFrozen(this)) return;          return super.clear();     } } 

If you need to work in ES5 environments, you could easily make a wrapper class for a Map rather than extending the Map class.

like image 169
loganfsmyth Avatar answered Oct 09 '22 09:10

loganfsmyth


@loganfsmyth, your answer gave me an idea, what about this:

function freezeMap(myMap){    if(myMap instanceof Map) {      myMap.set = function(key){       throw('Can\'t add property ' + key + ', map is not extensible');     };      myMap.delete = function(key){       throw('Can\'t delete property ' + key + ', map is frozen');     };      myMap.clear = function(){       throw('Can\'t clear map, map is frozen');     };   }    Object.freeze(myMap); } 

This works perfectly for me :)


Updated with points from @Bergi in the comments:

var mapSet = function(key){   throw('Can\'t add property ' + key + ', map is not extensible'); };  var mapDelete = function(key){   throw('Can\'t delete property ' + key + ', map is frozen'); };  var mapClear = function(){   throw('Can\'t clear map, map is frozen'); };  function freezeMap(myMap){    myMap.set = mapSet;   myMap.delete = mapDelete;   myMap.clear = mapClear;    Object.freeze(myMap); } 
like image 39
Tieme Avatar answered Oct 09 '22 09:10

Tieme