Search code examples
javascriptprototypeecmascript-5

Assign new property to empty object which has frozen prototype


Why can't I assign new properties to non-frozen object, which has frozen prototype:

Working without Object.freeze:

'use strict'
//This object will be prototype of next objects
var defaults = {
  name: 'def name', 
  sections: {
    1: {
      secName: 'def sec name'
    }
  }
};

//So we have an empty object with prototype set to our default object.
var specificObject = Object.create(defaults);


specificObject.sections = {};
console.log(specificObject.hasOwnProperty('sections')); //true

specificObject.sections['1'] = Object.create(defaults.sections['1']);

Above code works as expected, but I want to make sure that defaults won't be accidentally changed. So I want to freeze my defaults object:

'use strict'
//This object will be prototype of next objects
var defaults = {
  name: 'def name', 
  sections: {
    1: {
      secName: 'def sec name'
    }
  }
};

//!!!!!!!!!!!!
Object.freeze(defaults);

//So we have an empty object with prototype set to our default object.
var specificObject = Object.create(defaults);

//TypeError: Cannot assign to read only property 'sections' of #<Object>
specificObject.sections = {};
console.log(specificObject.hasOwnProperty('sections')); //true

specificObject.sections['1'] = Object.create(defaults.sections['1']);

What I don't get is why can't I assign to specificObject if its prototype is frozen?

//EDIT: Notice that specific object is not frozen:

'use strict'
//This object will be prototype of next objects
var protoObj = {a: 1, o: {}};
Object.freeze(protoObj);

console.log(Object.isFrozen(protoObj)); //true

var n = Object.create(protoObj);
console.log(Object.isFrozen(n)); //false

Solution

  • What I don't get is why can't I assign to specificObject if its prototype is frozen?

    Because property attributes are inherited. Yes, it's odd.

    If you freeze an object, it will set the [[writable]] attribute of all data properties to false.

    If you assign to an object property, but that property does not exist on the object, it will go and look it up on the prototype - it might be defined as setter there. When this lookup will return and say that there is a property of that name but it is non-writable, your assignment will fail (and throw in strict mode).

    What can you do against this?

    • Use Object.defineProperty instead of assignment, it doesn't check the prototype
    • or similarly, use the second parameter of Object.create to create the own property
    • freeze the defaults only after you've assigned to specificObject.