23

This example creates an object, freezes it, and then creates a new object from the frozen object. If the second object tries to change the test property, it can't. It remains frozen with the first object's value of 10.

//Create an object and freeze it

var first = {
    test: 10
};
Object.freeze(first);

//Create a second object from the first one and
//try and change the new test property (you can't)

var second = Object.create(first);
second.test = 20;
console.log(second.test); //10

Here are my questions:

Is second.test a new property on a new object, or is it just a reference to the property in the frozen first object?
Is it possible to use the frozen first.test as a default value, but let second.test overwrite it if it needs to?

My reason for asking is because I want to make an immutable a base object as a template with default values, and then use it to make new objects that I can customize. What's the best approach for this?

Thanks!

d13
  • 9,817
  • 12
  • 36
  • 44

4 Answers4

16

second is in fact a new object, with first being the prototype of second. The reason why

second.test = 20;

does not work is because upon assignment, it will look for the settings on the prototype (i.e. configurable, enumerable, writable, [[Extensible]]) and not assign to the instance if any of these are false1. To assign directly to the instance, you'll have to use Object.defineProperty on second:

var first = {
    test: 10
};
Object.freeze(first);

var second = Object.create(first);
Object.defineProperty(second, 'test', { value: 20, enumerable: true, configurable: true, writable: true });
console.log(second.test); // 20

1: [[Put]]: the ECMAScript Specification, §8.12.5

William Luc Ritchie
  • 1,666
  • 1
  • 11
  • 15
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
  • It seems then that 'second' behaves AS IF it was frozen too. It inherits its "frozennes". But if you call Object.isFrozen(second), that returns false I think – Panu Logic Jul 02 '20 at 02:35
12

Use Object.assign

         var first = {
            test: 10
        };
        Object.freeze(first);

        //Create a second object from the first one and
        //try and change the new test property (you can't)

        var second = Object.assign({}, first, {
            test: 20
        });
        console.log(second.test); //20
Gorilla
  • 553
  • 1
  • 6
  • 11
  • 3
    Pretty outside case, but note that this still won't work if the frozen object contains references to other frozen objects, and you try to overwrite _their_ properties. Still a pretty useful technique for simple data structures, just nobody get the idea that it's a foolproof way to create immutables. – Dtipson Jan 18 '16 at 16:55
2

In your case second is a reference to first (like you assumed). A solution would be to clone your object. There is no build in way to clone objects - you have to do it yourself, here is how (source):

function clone(obj){
   if(obj == null || typeof(obj) != 'object')
      return obj;

   var temp = obj.constructor();

   for(var key in obj)
       temp[key] = clone(obj[key]);
   return temp;
}

Then you use it this way:

var first = {
    test: 10
};
Object.freeze(first);

// clone it into a new one
var second = clone(first);
second.test = 20;
console.log(second.test); // 20 where the first is locked
Community
  • 1
  • 1
Dimitar Dimitrov
  • 14,868
  • 8
  • 51
  • 79
  • No built-in way to clone? Object.assign is pretty much a cloner, I'd say. –  Mar 16 '18 at 06:48
  • @loldrup well this was answered in 2013 and I believe Object.assign was introduced in the ECMAScript2015 spec, it requires polyfills even now for IE11 for example. Anyway I agree the answer should probably be updated/edited to include it as an option and to get with the times. – Dimitar Dimitrov Mar 16 '18 at 06:54
  • Juts to be clear it is not true that "... second is a reference to first". As said in the accepted answer "first is the prototype of second" – Panu Logic Jul 02 '20 at 02:39
  • @PanuLogic although the terminology you used is indeed better in the context of JS (I agree). However the 2 phrases mean the same thing from what I understand? Is there a semantic difference? I mean in JS being the “prototype of” is “having a reference to”, is it not? – Dimitar Dimitrov Jul 02 '20 at 06:55
  • Yes words can have many different meanings. The way I understand it is when you have a "reference" you are in effect talking about the "same thing". Whereas the prototype of an object is another object, separate from the object it is the prototype of. The prototype may have fewer properties than the object it is is the prototype of, and the values of the properties can be different. An object "A" whose prototype is object :"B" does have a reference to "B". But "B" does not have a reference to "A". – Panu Logic Jul 13 '20 at 02:27
1

In new versions of javascript you can use object destructuring to create a new object with the properties of the old one.

const first = {
    test: 10
};
Object.freeze(first);

//Clone the object
const second = {...first};
second.test = 20;
console.log(second.test); // 20 where the first is locked