Search code examples
javascriptecmascript-5

Overriding object getter without defineProperty


I've defined a property with a getter using Object.defineProperty and i'd like to be able to overwrite it without needing to use defineProperty to prevent any confusion for coworkers who are going to consume it but may not know it's not a a "normal" property. I've tried the writeable and configurable options to no avail.

var fixture = {
  name: 'foo',
  color: 'green'
};

Object.defineProperty(fixture, 'email', {
  get: function () {
    return 'test+' + Date.now() + '@gmail.com';
  },
  configurable: true,
  writeable: true
});

console.log(fixture.email); // test+<date>@gmail.com
fixture.email = 'bob'; // consumer attempts to overwrite
console.log(fixture.email); // test+<date>@gmail.com T_T

// this _will_ modify the property
Object.defineProperty(fixture, 'email', {
  value: 'overwritten'
});
console.log(fixture.email);


Solution

  • I've tried the writeable and configurable options to no avail.

    writable is only available for data properties, i.e. the ones that have a value not getter and setter.

    configurable is only to allow deletion and redefinition, not to make the property settable.

    If you want fixture.email = 'bob'; not to throw an error, you'll have to provide a set method on your attributes object. This method may now:

    • ignore the new value
    • store the new value, e.g. in a different property or a closure variable, so that the getter can yield it on subsequent accesses (@Icepickle has some examples of this in his answer)
    • convert the accessor property back to a "normal" one

    The last is probably the easiest and most rewarding choice:

    var fixture = {
      name: 'foo',
      color: 'green'
    };
    
    Object.defineProperty(fixture, 'email', {
      get: function() {
        return 'test+' + Date.now() + '@gmail.com';
      },
      set: function(val) {
        Object.defineProperty(this, 'email', {
          value: val,
          writable: true
        });
      },
      configurable: true,
      enumerable: true
    });
    
    console.log(fixture.email); // test+<date>@gmail.com
    fixture.email = 'bob'; // consumer attempts to overwrite
    console.log(fixture.email); // bob