Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I set [enumerability and] writability of unconfigurable property descriptors?

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty states:

configurable: True if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object. Defaults to false.

So, I have a

var x = Object.defineProperty({}, "a", {
    value:true,
    writable:true,
    enumerable:true,
    configurable:false
});

Now I can play with x.a = false, for(i in x) etc. But even though the descriptor is should be unconfigurable, I can do

Object.defineProperty(x, "a", {writable:true}); // others defaulting to false
Object.defineProperty(x, "a", {}); // everything to false
Object.freeze(x); // does the same to the descriptors

The other way round, setting them to true again, or trying to define an accessor descriptor, raises errors now. To be exact: Object.defineProperty: invalid modification of non-configurable property.

Why can I "downgrade" descriptors though they say they were non-configurable?

like image 716
Bergi Avatar asked Mar 22 '12 20:03

Bergi


1 Answers

First, even when configurable is false, writable can be changed from true to false. This is the only attribute change allowed when configurable is false. This transition was allowed because some built-in properties including (most notably) the length property of arrays (including Array.prototype) are specified to be writable: true, configurable: false. This is a legacy of previous ECMAScript editions. If configurable: false prevented changing writable from true to false then it would be impossible to freeze arrays.

Object.defineProperty doesn't work quite like you're assuming. In particular, how it processes the property descriptor works differently depending upon whether or not the property already exists. If a property does not exist, the descriptor is supposed to provide a definition of all attributes so any missing attributes in the descriptor are assigned default values before the descriptor is used to create the property. However, for an already existing property the descriptor is taken as a set of delta changes from the current attribute settings of the property. Attributes that are not listed in the descriptor are not changed. Also, a attribute that has the same value in the delta descriptor as the current property attribute value is also consider no change. So the following are all legal:

Object.defineProperty(x, "a", {writable:false}); // can always change writable to false.
                                                //others attributes, not changed
Object.defineProperty(x, "a", {});     // no attributes, so nothing changes
Object.freeze(x); // same as Object.defineProperty(x, "a", {writable:false});
Object.defineProperty(x, "a", {enumerable:true, configurable: false}); //no change, 
like image 101
Allen Wirfs-Brock Avatar answered Nov 10 '22 00:11

Allen Wirfs-Brock