6

I am trying to solve a problem that came to my mind lately. Let's say we would want and would know how to make a point in having dynamic getters and setters in javascript, more like those in php (__get, __set). But as javascript does not have a catch-all property the only thing we could do is to provide a list of possible keys and iterate to add getters and setters on those only, and hope none other will ever come.

But the problem is not by far solved. So the next approach that came to my mind was to use a nasty hack with try and catch, so anytime a name would be undefined in an object to use the catch as a getter (at least) and then resume the code, hard and maybe pointless thing to do. But from here came my second problem, in an use such as this :

console.log(g.someundefinedproperty); 

the result would have been a call to console.log showing undefined with no exception to be ever thrown. And then it came to me: what if I would use the original window.undefined getter and setter, after all it must be called every time I screw up and misspell a word or something.

So I tried

Object.defineProperty(window, 'undefined', {
    get : function ()
    {
         // functional code, including getting the caller and figuring out
         // where we are, and what we have to do... easy :D

         console.log('works');
    },
    set : function ()
    {
         // some couple more fine hacks here
         console.log('this too');
    }
});

But unfortunately the undefined property of window is configurable : false. Other hacks tried were cloning the window object except the undefined and the inner window property. And on the new object to define the new undefined (please take note of the irony) and then window = mybetterwindow;

As this did not rose any issue my hopes were high, but yet again the system failed me as window can't be overwritten, by design. I made a guess it has it's own getter and it's reinstanciate itself based on the prototype found in window.prototype or even better Window.prototype (note the uppercase).

As my final step in this experiment I had redefined undefined on this prototype hitted run. To no avail, nothing was changed... I tryed creating a new Window(), but Window is not a constructor, fail!

As I have run out of ideas I find myself here writing this pleading for help. If you have any ideas how to solve the dynamic getters and setters problem, (the existencial problem of life, universe and everything else), in a way which does not modify in any way the... way I use the objects (and as bonus it doesn't need to break a hole in the fabric of time and space) or the syntax, I entreat you to speak or forever be silent :).

BenMorel
  • 30,280
  • 40
  • 163
  • 285
helly0d
  • 818
  • 2
  • 9
  • 27
  • Do you have a real life problem you're trying to solve? – Ash Burlaczenko Dec 12 '12 at 22:15
  • I think redefining `undefined` does risk breaking a hole in the fabric of time and space. Maybe that's why it's been forbidden in ES5. This is an interesting question, however I foresee most answers and comments will be telling you that catch-all accessors are not needed, and harmful. Heck, you're even citing PHP as an example! :P – bfavaretto Dec 12 '12 at 22:15
  • Of course I do, no point in here if I wouldn't have. It's the design I must follow for the framework I am working on. – helly0d Dec 12 '12 at 22:16
  • @bfavaretto Well, PHP is a perfect example of what can be done, and I trully need it now. – helly0d Dec 12 '12 at 22:17
  • @helly0d That was just a joke! As I said, I'm interested by the issue you raised. – bfavaretto Dec 12 '12 at 22:19
  • @bfavaretto Well, we can start a full debate on why you should be able to do it, and what power it can give :). But the problem is still up, hopefully somebody can answer it. And god knows I tried everything. – helly0d Dec 12 '12 at 22:22
  • 3
    The only way to do what you want, is using ECMAScript 6 Proxies, they are usable on modern engines (IIRC, they are available on FF4+ and Chrome 19+). See also: http://stackoverflow.com/questions/2266789/is-there-an-equivalent-of-the-nosuchmethod-feature-for-properties-or-a-way/3757676#3757676 – Christian C. Salvadó Dec 12 '12 at 22:28
  • 1
    @helly0d Yeah, god do know you tried :D. I am also very interested in this issue. – khael Dec 12 '12 at 22:33
  • FYI, I got curious, did some playing, and wrote a brief blog entry that you *may* find helpful or interesting. And, I'm pretty sure, unlike the hasty, fog-brained answer I posited earlier, it actually touches on your question! http://blog.svidgen.com/2012/12/getters-setters-and-redefining.html – svidgen Dec 17 '12 at 17:23
  • @svidgen Interesting for someone who won't be using that code, because in a wide application i would have to wrap the entire application in the function to be rewritten and then `eval`ed to which point i lose time and memory. I have a friend ( @khael ) who does some kind of Monkey Patching using the same technique but in PHP and does it only once per project, after which saves the project as being Monkey Patched. – helly0d Dec 18 '12 at 00:24
  • When you're relying on getters and setters, or a scripted language for that matter, you've already decided to sacrifice on both. At least in this case, or with similar server preprocessing, you take the big hit at and only at "load" time, as you already do with scripted languages. – svidgen Dec 18 '12 at 00:54
  • @svidgen That's the point of setters and getters. I already have the data loaded somewhere in memory but i want to get it or set it only when need it so i have this advantage of doing the sorting and some other stuff only when i ask for it, but with your `self writing` accessor it's a pain in the a** for the browser to have the entire application rewritten and reinterpreted, and the debugging would be also painful, but thanks for the idea. – helly0d Dec 18 '12 at 01:01
  • It's not as big of a hit as you might imagine. And debugging will be similar to what you'd experience using closures. But, I won't press if you're convinced it's unworkable. It was just a thought. And the only fully cross-browser solution I can think of. – svidgen Dec 18 '12 at 01:09
  • I'd be more quick to argue against it on account of the difficulty you'd have getting that rewriting in a state of high functionality. But, if one could invest some time there, I wouldn't be too concerned with efficiency for most apps. – svidgen Dec 18 '12 at 01:14

2 Answers2

5

But unfortunately the undefined property of window is configurable: false

This is true only since EcmaScript 5.1. Before, it was overwritable.

what if I would use the original window.undefined getter and setter, after all it must be called every time I screw up and misspell a word or something.

No, it would not have worked. There is a difference between the undefined value and the global variable "undefined". The variable is not evaluated each time a undefined value is encountered (e.g. in typeof (void 0)), only when you use it explicitly (such as in g.someprop === undefined).

Any ideas how to solve the dynamic getters and setters problem?

There is only one solution: Proxies. Unfortunately, it is only a harmony draft and currently only supported in Firefox' Javascript 1.8.5.

See also Is there an equivalent of the __noSuchMethod__ feature for properties, or a way to implement it in JS?, Detect when a new property is added to a Javascript object? or How to detect when a property is added to a JavaScript object? which implements a polling solution (checking for changes via setInterval).

For a clean solution, you currently have to use an explicit getter function, which you pass the property name (g.get("someundefinedproperty")).

Community
  • 1
  • 1
Bergi
  • 513,640
  • 108
  • 821
  • 1,164
2

What you're trying to accomplish (a global getter for any undefined property) is outside the scope of cross-browser JavaScript.

The closest functionality would be using __defineGetter__ to add a particular property to all objects:

var o = Object.prototype;
o.__defineGetter__("testo", function() { return "yo in testo"; });
alert({}.testo);
alert((new Date()).testo);

http://jsfiddle.net/NLCvs/2/

This may be useful if you can predict the name of possibly undefined properties ahead of time. Even so it's quite hacky and bad form to add an accessor to all objects for a property that might be undefined.

A better option is to refactor code when an undefined property may be accessed:

function getter(o, n) {
 return typeof o[n] == 'undefined' ? 'some default value' : o[n];
}
var obj = { hello: 'world' };
alert(getter(obj, 'hello'));
alert(getter(obj, 'an_undefined_property'));

An even better option is to use a mixin to set any default properties you will need to access, for instance via jQuery.extend

var defaults = {
 required: 'this property is required'
};
var my_object = {
 something_else: 'this is something else'
};
var o = jQuery.extend({}, defaults, my_object);

// o.required === 'this property is required'
// o.something_else === 'this is something else'
leepowers
  • 35,484
  • 22
  • 93
  • 127