Rob Eberhardt

cleverness ensues

skip navigation

 Friday, April 01, 2005

I just asked a guru of advanced Object-Oriented Javascript, Douglas Crockford the following question.  For posterity, and for other possible takers, I'm posting it here too.

Also, for the record, this has nothing to do with IE's proprietary -- but wonderful -- setExpression method or CSS expression capabilities.  Those dynamic properties only apply to DOM objects, not custom Javascript objects.


Do you know of a way to define dynamic object properties in Javascript?

For example, VBScript lets us define classes such as this:

CLASS myClass
    PUBLIC phrase
    PUBLIC PROPERTY GET firstword    'get first word from phrase
        firstword = left(phrase, instr(phrase, " "))
    END PROPERTY
    PUBLIC PROPERTY LET firstword(v) 'set phrase's new first word
        phrase = v & " " & phrase
    END PROPERTY
END CLASS

This demonstrates two important features:
1. the firstword property can return dynamic results (depending on the current value of the phrase property). 
2. setting the firstword property can run other code (which dynamically prepends to the phrase property).

We can fully accomplish #1 (Property Get) in Javascript, by reassigning the toString method/property to a function like so:

function myConstructor(){
    var self = this;    //preserve object's context
    this.phrase = '';
    this.firstword = function(v){
        if(v){self.phrase = v + ' ' + self.phrase};    //LET
        return self.phrase.substring(0, self.phrase.indexOf(' ')); //GET
    }
    this.firstword.toString = this.firstword;
}

...But I've found no way to achieve #2 (Property Let or Set) in Javascript.  I can set firstword as a method, but setting the property value overwrites the method definition (and all subsequent GETs return that static value).

I've extensively searched, but found no answer (at least not before Javascript 2.0, which doesn't yet exist).  Any ideas from the experts?

Update: Douglas Crockford responded that he doesn't care for getter/setter properties, since "it allows simple assignment to have side effects, which I think works against reliability."  (I take that as a "No, it's not possible.")
    My current need is that I'm patterning my custom object after part of the DOM, whose objects certainly do have getter/setters.  I agree with Douglas that getter/setter side effects can be dangerous (esp. in the hands of a poor coder, as with any powerful code construct), but I think the use of methods -vs- getter/setter properties should be in the realm of "best practice" rather than a language limitation. 

4/1/2005 3:25 AM Eastern Daylight Time  #    Disclaimer  |  Comments [6]  | 
8/23/2006 11:20:29 PM (Eastern Daylight Time, UTC-04:00)
Can you provide an example of using #2. I'm trying to envision a need for it. I'll confess to trying to avoid VBScript like the plague, although lately that has been impossible. The shop that I'm at had no idea that VBScript even supported OO.

Personally the idea of class field accessors having side effects makes me nervous. I like my classes to be such that every field has a getter that gets and a setter that sets and nothing else (JavaBeans style). Any kind of programatic stuff is done through seperate well documented methods. I know that Perl cat get more contextual but that always makes me nervous. This is especially true for classes that I dub data objects. I like them to be real tight. If you're sure of the quality of your data, you can relax a lot more with classes that play with it.

BTW, I agree with you about getter/setter methods. For me they are the Class's contract police insuring that what goes in is valid and in some cases saving memory by only instantiating portions of the Object when needed in getters. But this is all coming from being a Java type.

BTW, found your blog through http://www.feedmap.net/blogmap/neighblogs.aspx?feed=http://blog.cbaker.org/wp-atom.php . Heynow!
8/23/2006 11:20:29 PM (Eastern Daylight Time, UTC-04:00)
Regarding #2:
I mentioned "My current need is that I'm patterning a custom object after part of the DOM, whose objects certainly do have getter/setters."
To explain: I'm working on a wrapper object which exposes an checkboxes/radio button arrays in a similar manner to a SELECT object's options. For example, SELECTs have directly-accessible selectedIndex and value properties. To get the equivalent info with radio buttons normally requires looping script. Another common checkbox/radio button operation I wanted to simplify is that of assigning event handlers (again without custom looping code).

Yes, I can write a reusable function to accomplish these (and have), but I wanted to wrap it all up into a single convenient object instantiation. Besides less code, it would also make it easier to switch "glue code" between SELECTs and checkbox/radio button arrays (which are interchangeable from a UI standpoint).

Native SELECT and other DOM objects DO support setter properties, so I wanted to do the same for consistency. Otherwise, I wouldn't normally create setter properties in Javascript, opting instead for set methods. I have it partially working (gets but not sets).

It disappoints me that Javascript, an astonishingly powerful OO scripting language, loses to simple old VBScript in a matter like this (dates too, but that's another story).

Cool about the Feedmap link (that got me to waste a lotta time!). For some reason I had the wrong ICBM coords, so it thought I was in Warren County.

Don't miss GeoURL either: http://geourl.org/near?p=http://throbs.net/
8/23/2006 11:20:29 PM (Eastern Daylight Time, UTC-04:00)
Mozilla supports ES4 getters and setters, but as you say, that's a work in progress.

function myConstructor(phrase) {
this.phrase = phrase;
}
myConstructor.prototype = {
get firstword() {
return this.phrase.substring(0, this.phrase.indexOf(' '));
},
set firstword(word) {
this.phrase = word + ' ' + this.phrase;
}
}
var obj = new myConstructor('hello world');
alert(obj.firstword);
8/23/2006 11:20:30 PM (Eastern Daylight Time, UTC-04:00)
That's great, thanks Jonathan.

Indeed, adding this works in Firefox as I would hope:
obj.firstword = 'hi';
alert(obj.firstword); // alerts 'hi' (without overwriting)

I hadn't realized that was implemented yet in Mozilla/Firefox. Very cool!
8/23/2006 11:20:30 PM (Eastern Daylight Time, UTC-04:00)
I talk about this a bit here: <a target="_new" href="http://blogs.acceleration.net/birdman/archive/2005/04/28/1055.aspx">http://blogs.acceleration.net/birdman/archive/2005/04/28/1055.aspx</a>

Jonathan Watt demonstrated one way above, there is another way as well:
var o = {i:0} //declare object
//add a getter property at 'foo'
o.foo getter = function() {this.i++; return &quot;foo&quot; + i; }
o.foo //returns "foo1", "foo2" ....

Drawbacks:
*This only works in Mozilla.
*I have not been able to find a way to get a reference to that getter/setter function. Any time you try to access it you just get the evaluated value. If you needed to pass the function into another function or wish to inspect the function source you seem to be out of luck.

On Doug Crawford's note:
I agree with Rob that this should be more a matter of good practice: don't do anything unexpected for the external programmer like return a different value from the setter function than one that was passed in.
o.foo = o.bar = "foobar"; // o.foo should be set to the value "foobar" regardless of whether o.bar is data slot or a property.
8/23/2006 11:20:30 PM (Eastern Daylight Time, UTC-04:00)
That's great. I wish *other browsers* would get on the bandwagon.

I just saw an interesting tidbit here: http://blogs.acceleration.net/russ/archive/2005/07/21/1834.aspx
(via a ping on your article, Nathan).
Looks like someone found Javascript's internal get/set functions. Gotta toy with that one soon!
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):