Search code examples
javascriptprototype-programming

Revert Object.defineProperty getter to original state, that is normal constant property


I am trying to implement status effects in a way so that there will be little or no if cases in the game program, that is, the effects should apply themselves on the objects.

I created a simple test case, the basic skeleton of my plan:

// This can represent a game object - player or monster
var test = {damage: 20};
// For the sake of simplicity, I just define
// EffectMissfortune without inheriting other classes
function EffectMissfortune() {

}
/**
 * Applies effect on an object, altering it's properties 
 * until remove is called **/
EffectMissfortune.prototype.apply = function(obj) {
  // Remember oridinal damage
  obj._damage = obj.damage;
  // Define property getter
  Object.defineProperty(obj, "damage", {
    // Has about 40% chance to return 50% damage
    get: function() {
      if(Math.random()<0.4) {
        return this._damage/2; 
      }
      else
        return this._damage;
    },
    // This should allow me to overwrite this, right?
    configurable: true
  });
}
/**
 * Removes anything that was set aby `apply` method**/
EffectMissfortune.prototype.remove = function(obj) {
  obj.damage = obj._damage;
}

I just wrote that in Firebug console and I omitted lot's of stuff like remembering applied status effects and so on. The important thing is that my .remove method doesn't work:

// Test:
console.log("No effect: ");
console.log(test.damage, test.damage, test.damage, test.damage, test.damage, test.damage, test.damage, test.damage);
// Apply effect
var effect = new EffectMissfortune();
effect.apply(test);
console.log("Missfortune: ");
console.log(test.damage, test.damage, test.damage, test.damage, test.damage, test.damage, test.damage, test.damage);
effect.remove(test);
// Effect removed?
console.log("No effect: ");
console.log(test.damage, test.damage, test.damage, test.damage, test.damage, test.damage, test.damage, test.damage);

And output:

No effect: 
20 20 20 20 20 20 20 20
Missfortune: 
20 10 10 10 10 10 20 10
No effect: 
10 10 20 20 10 20 20 20

So the first simple question is:

  • How to remove anything that was set by last Object.defineProperty call?

The second harder one is:

  • If I was to apply multiple effects at a time, I'd like to chain anything they defined. In that case, can I retrieve current Object.defineSettings (get, set, configurable, enumerable and property actual value) before calling my own Object.defineProperty? Something like Object.getPropertySettings?

Solution

  • You have to set the writable property to true if you want to be able to override the property value with a simple assignment (=). The configurable property will allow the property to be altered by a subsequent call to Object.defineProperty() but not by assignment.

    editHowever, a property cannot be both writable and have accessor methods. In this case, then, you can remove the value with another .defineProperty() call:

    EffectMissfortune.prototype.remove = function(obj) {
      Object.defineProperty(obj, "damage", {
        configurable: true,
        writable: true,
        value: obj._damage
      });
    }
    

    As to your second question, you can get a list of the properties with Object.keys() and then use Object.getOwnPropertyDescriptor() for each property.