0

Is there a way to catch a Javascript set function in defineProperty, perform some logic and then decide whether to actually allow the original set function to be invoked?

Example

var scope = {}; 
scope.myVar = 1;

scope._myVar = scope.myVar; 
Object.defineProperty(scope, "myVar", {  
  get: function(){
    return scope._myVar;
  },
  set: function(val) {
    scope._myVar = val;
    //Do some other work   
  } 
}

//Now when scope.myVar is changed, its setter is invoked 
//I would like to write some code here now that will run even before the
//myVar setter, do some work, and then decide whether to invoke the setter
//or not.  If it decides to not invoke the setter, then it will be as
//though the scope.myVar = ... was never called.

//Psuedo-code
scope._setMyVar = scope.setMyVar;
scope.setMyVar = function(val) {
  //do some work
  var condition = resultOfWorkAbove;

  if(condition) {
    scope._setMyVar(val);
  }
}
Jeremy Friesen
  • 383
  • 1
  • 3
  • 13

1 Answers1

2

Yes, there is. You can get the old setter (what you've written as scope._setMyVar = scope.setMyVar; in your pseudocode) with Object.getOwnPropertyDescriptor().

(function(obj, prop) { // an IEFE for local variables
    var desc = Object.getOwnPropertyDescriptor(obj, prop),
        oldsetter = desc.set;
    desc.set = function(val) {
        var condition = … // do some work;
        if (condition)
            oldsetter.call(this, val);
    };
    Object.defineProperty(obj, prop, desc);
}(scope, "myVar"));

Of course, this does only work if the original property descriptor had configurable set to true, otherwise we cannot overwrite it.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Awesome work @Bergi. I had no idea. This is very pwoerful and I assume incredibly easy to aim at my own foot. :) – Jeremy Friesen Sep 04 '14 at 18:28
  • 1
    Yeah, calling this on a data property (not an accessor one) will hurt for example :-) – Bergi Sep 04 '14 at 18:33
  • I'm running this code on IE10 without setting configurable to true and it's running fine. When I do a look-up on the configurable property, I see it defaults to false. Weird. – Jeremy Friesen Sep 04 '14 at 19:24
  • 1
    `defineProperty` is running without throwing in sloppy mode, yes, but the new setter should not be in effect then. Or does IE10 really overwrite the setter and `// do work` is executed? – Bergi Sep 04 '14 at 19:41
  • Unless I'm misreading my code and have inadvertently done something to correct that, `//do some work` is executing – Jeremy Friesen Sep 04 '14 at 20:15