thomasdclark
thomasdclark

Reputation: 484

Setting Javascript property attributes directly instead of Object.defineProperty

I know that the proper way to set Javascript property attributes is to use the Object.defineProperty function, but I am curious what is preventing the setting of these values directly on the descriptor object that is returned via Object.getOwnPropertyDescriptor.

var a = new Object()
a.x = 1
var attributes = Object.getOwnPropertyDescriptor(a, 'x') //Object {value: 1, writable: true, enumerable: true, configurable: true}
var attributesOfWritable = Object.getOwnPropertyDescriptor(attributes, 'writable') //Object {value: true, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(a, 'x').writable = false
console.log(Object.getOwnPropertyDescriptor(a, 'x')) //Object {value: 1, writable: true, enumerable: true, configurable: true}
Object.defineProperty(a, 'x', {writable: false})
console.log(Object.getOwnPropertyDescriptor(a, 'x')) //Object {value: 1, writable: false, enumerable: true, configurable: true}

As shown in the code above, when looking at the descriptor object returned for the 'writable' property on the original descriptor object for a.x, the property is writable and configurable, which means setting the writable property of the property descriptor didn't change the underlying x property.

So I am unsure why I cannot just write:

Object.getOwnPropertyDescriptor(a, 'x').writable = false

Upvotes: 3

Views: 467

Answers (1)

Oriol
Oriol

Reputation: 288550

That's because each time you use Object.getOwnPropertyDescriptor, FromPropertyDescriptor builds a new different object.

That object has no special setters, so altering its data won't affect the properties of the original object.

Instead, you should redefine the property:

var desc = Object.getOwnPropertyDescriptor(a, 'x');
desc.writable = false;
Object.defineProperty(a, 'x', desc);

Otherwise, you could build you own API, something like this

var getLiveDescriptor = (function() {
  var map = new WeakMap(),
      getDesc = Object.getOwnPropertyDescriptor;
  return function getLiveDescriptor(obj, prop) {
    var descriptors = map.get(obj);
    if(!descriptors) map.set(obj, descriptors=Object.create(null));
    var descriptor = descriptors[prop];
    if(descriptor) return descriptor;
    return descriptors[prop] = new Proxy({}, {
      has(target, key) {
        return key in getDesc(obj, prop);
      },
      get(target, key, receiver) {
        return getDesc(obj, prop)[key];
      },
      set(target, key, value, receiver) {
        var desc = getDesc(obj, prop);
        desc[key] = value;
        Object.defineProperty(obj, prop, desc);
        return true;
      },
      ownKeys(target) {
        return Object.getOwnPropertyNames(getDesc(obj, prop));
      }
    });
  };
})();
Object.getOwnPropertyDescriptor(a, 'x').writable; // true
getLiveDescriptor(a, 'x').writable = false;
Object.getOwnPropertyDescriptor(a, 'x').writable; // false

Upvotes: 4

Related Questions