Access default getter/setter for JavaScript object
Asked Answered
W

4

7

JavaScript getters and setters can be overridden for specific properties using Object.defineProperty. Is there some way to access the default getter/setter (i.e. the function used if the getter/setter has not been overwritten)? In my custom setter I want to have special processing in some cases, but use the default processing in others. I tried:

Object.defineProperty(obj, 'foo',
  'set': function(val) {
    if (useCustom) {
      doCustomProcessing(obj, 'foo', val);
    }
    else {
      obj['foo'] = val;
    }
  }
);

Unfortunately this leads to a stack overflow since obj['foo'] = val; causes the custom setter to be invoked. So I'd like to find a way to set the foo property of obj using the default setter. Is this possible?

Wellbeing answered 25/11, 2014 at 15:30 Comment(0)
H
8

As far as I know, a property either has a value (data property), or it has getter/setter (accessor property): it cannot have both. Use a local variable under closure, or another property, as an underlying storage for properties where you have a getter/setter defined.

For example,

(function() {
    var value;
    Object.defineProperty(obj, 'foo', {
      set: function(val) {
        if (useCustom) {
          value = doCustomProcessing(obj, 'foo', val);
        } else {
          value = val;
        }
      },
      get: function() { return value; }
    });
})();
Hearsay answered 25/11, 2014 at 15:37 Comment(1)
@OP, To see more about data properties vs. accessor properties, see ECMAScript 5, section 8.6.Trafalgar
Q
7

In ES6 you can use a Proxy object:

var myObject = {};

var myProxyObject = new Proxy(myObject, {
  set: function(ob, prop, value) {
    if (prop === 'counter' && value === 10) {
      // Some specialised behaviour
      ob.counter = 0;
    }
    else {
      // Default setter behaviour.
      ob[prop] = value;
    }
    // Return true otherwise you will get a TypeError in strict mode.
    return true;
  }
});

>>> myProxyObject.counter = 5;
>>> console.log(myProxyObject.counter);
5

>>> myProxyObject.counter = 10;
>>> console.log(myProxyObject.counter);
0
Quartana answered 17/8, 2016 at 4:47 Comment(0)
C
1

The only way I know how to do that is to make the variable non-private, but two examples, the second more terse:

(function testInheritance(global, doc) {
  "use strict";
  var MyFunc = Object.create({}, {
      _foo: {
        value: "Some Default Value",
        writable: true,
        enumerable: true
      },
      foo: {
        get: function() {
          return this._foo;
        },
        set: function(value) {
          this._foo = value;
        }
      }
    }),
    testFunc = Object.create(MyFunc);
  console.log(testFunc.foo); // "Some default value"
  testFunc.foo = "boo";
  console.log(testFunc.foo); // "boo";
 testFunc._foo = "Not a private variable anymore";
 console.log(testFunc.foo); // "Not a private variable anymore"
}(window, document));

(function testInheritanceTwo(global, doc) {
  "use strict";
  var MyFunc = Object.create({}, {
      foo: {
        get: function() {
          if (!this._foo) {
             return "Some default value set by the getter.";
          }
          return this._foo;
        },
        set: function(value) {
          this._foo = value;
        }
      }
    }),
    testFunc = Object.create(MyFunc);
  console.log(testFunc.foo); // "Some default value set by the getter."
  testFunc.foo = "Whomp";
  console.log(testFunc.foo); // "Whomp";
 testFunc._foo = "Not a private variable anymore, unfortunately.";
 console.log(testFunc.foo); // "Not a private variable anymore"
}(window, document));

So far as I can tell:

  1. You can't reference the value with the same name as what you use in set: function(value) or you end up with an infinite loop where setting the value calls the set value and it calls itself again, and so forth. Hence, your problem.

  2. If you try to make the variable _foo to private, then the setter does not work. With this syntax it seems you can hide the variable, but you cannot really make it private.

Cressler answered 13/6, 2015 at 22:11 Comment(0)
A
0

You can do something like:

const counter = new class {
    count = 1;
    toString () {
        return this.count++;
    }
}()

console.log(`${counter}`')

const myString = new class {
    toString () {
        return 'This is my string';
    }
}();

console.log(`${myString}`);
Aneroidograph answered 9/3, 2021 at 23:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.