Gerome Bochmann
Gerome Bochmann

Reputation: 494

Creating decorators with access to closures

I have lots of little objects that all need to fulfill very similar tasks and I would like to use a decorator to modify those objects since this makes things a lot easier for me. Problem is, I also need to trigger stuff as soon as certain data on these objects is accessed while at the same time preventing outside access to the actual data. Therefore, I'm using a closure to store the private data and provide magical mutators for outside access. Finally, I'm also using a constructor for no specific reason.

This is a very reduced version of the object and the parts that give me headaches:

var Foo = function () {
  var hiddenState;
  return {
    set state (s) {
      // Do something that justifies using magical accessors
      state = s;
    },
    get state () {
      // Do something that justifies using magical accessors
      return state;
    }
  }
};

var o = new Foo();
o.state = 10;

Okay, now there are some objects that should behave a different from the rest when their mutators are used. So, I thought, I'd just overwrite the mutators and that's it:

// Let's suppose I have some reason to modify the magical accessors for some objects but not for others
Object.defineProperty(o, 'state', {
  get: function () {return hiddenState /*...*/},
  set: function (s) {state = s /*...*/}
});

o.state; // Ouch!

This doesn't work. I get a reference error because I'm trying to access hiddenState when it wasn't defined within the scope of the new getter/setter pair.

I suspect JS dumps the closure that was only accessible from the old mutators as soon as I pass in the new ones. Is there some way to deal with this problem?

Upvotes: 0

Views: 58

Answers (1)

Bergi
Bergi

Reputation: 664548

You can access the closure that had access to hiddenState using Object.getOwnPropertyDescriptor. Also, if you don't need to modifiy the getter, just don't overwrite it at all, it will persist.

var oldSetter = Object.getOwnPropertyDescriptor(o, 'state').set;
Object.defineProperty(o, 'state', {
  set: function (s) {
    /* do something to s */
    oldSetter(s);
  }
});

Upvotes: 1

Related Questions