An experiment using an object literal based class definition pattern

Allen Wirfs-Brock allen at wirfs-brock.com
Thu Aug 4 23:00:59 PDT 2011


On Aug 4, 2011, at 8:39 PM, Axel Rauschmayer wrote:

>> From: Allen Wirfs-Brock <allen at wirfs-brock.com>
>> However, as current specified the [[Prototype]] of bar.prototype will be Object.prototype.  In other words,  bar inherits from foo but bar.prototype doesn't inherit from foo.prototype.  That seems unlikely to be the desired behavior in most situations.  We can fix that by specifying that if the RHS of <| is a function expression and the LHS is an object with a "prototype" property then the object that is the value of the "prototype" property of the new function object inherits from LSH.prototype rather than Object.prototype.
> 
> That is really cool, because it’ll give you inheritance for class methods and for instance methods (with the two being separate).
exactly!
> 
>> Another idea was an alternative way to express the "extend" operator for literal property definitions.  Doug Crockford suggested the following syntax:
>> 
>>     obj.{
>>        prop:1,
>>        prop:2
>>        }
> 
> It looks nice, but the operator should also work for a RHS that is not an object literal.

But using . as the operator it would be ambiguous with normal property access.  A different and unique operator wouldn't have the problem but also wouldn't look so nice and the primary motivation for the operator is for use in expression such this post is about.  An alternative that could work would be to allow a parenthesized expression to follow the dot.

> ...
> 
> How about rewriting this as follows?
I did something similar in my first go at this, but there is a problem...

> 
>>   const SkinnedMesh = THREE.Matrix4.Mesh <| function(geometry,materials){
>>     super.construtor(geometry,materials);
>>     this.{
>>       identity.Matrix: new THREE.Matrix4(),
>>       bones: [],
>>       boneMatrices: []
>>     };
>>   }.{
>>     default(){
>>       return new this(THREE.defaultGeometry,THREE.defaultMaterials);
>>     }
>>   }.prototype.{
>>     update(camera) {
>>       ...
>>       super(update);
>>     }
> 
>>   }; // possibly: }.constructor;

The value of the above expression that gets bound to SkinnedMesh is the prototype, not the constructor.   For a "class" definition you want that value to be the constructor object, not the prototype.  As your comment mentioned, to get this you would need to add a .constructor to the end.  I suspect that there would be a strong tendency for programmer to forget to do this. I think always following the pattern I identified is what people would want always want to do.  Plus the progress of constructor+per instance state, then prototype properties, and finally constructor properties seems like the right ordering from most important to least important.

> 
> It seems more intuitive to me to add the constructor methods directly to the constructor, instead of returning to that function via prototype.constructor. The final ".constructor" is not as elegant as I would like, though. 

To me, the .prototype,{  and .constructor.{ feel very much like prototype: and class: compartment labels of the sort that was discussed a couple weeks ago.


> 
> Another possibility: allow "extend" for properties in object literals (kind of a recursive overriding).
> 
>>   const SkinnedMesh = THREE.Matrix4.Mesh <| function(geometry,materials){
>>     super.construtor(geometry,materials);
>>     this.{
>>       identity.Matrix: new THREE.Matrix4(),
>>       bones: [],
>>       boneMatrices: []
>>     };
>>   }.{
>>     default(){
>>       return new this(THREE.defaultGeometry,THREE.defaultMaterials);
>>     },
>>     prototype.{
>>       update(camera) {
>>         ...
>>         super(update);
>>       }
>>     }
> 
>>   };

Yes that could work, but it buries the prototype properties deep inside the constructor properties.  I again, I think the flat constructor-instance properties-consturctor properties sequence will be the easiest to teach, remember, and use.
> 
> One more idea: If the constructor is becoming the class and has properties of its own, perhaps there is a way of writing a constructor function as an object literal, by giving an object literal a callable “body”.
> 
>> const SkinnedMesh = THREE.Matrix4.Mesh <| {
>>     function(geometry,materials) {
>>         super.constructor(geometry,materials);
>>         this.{
>>             identity.Matrix: new THREE.Matrix4(),
>>             bones: [],
>>             boneMatrices: []
>>         };
>>     }
>>     default() {
>>         return new this(THREE.defaultGeometry,THREE.defaultMaterials);
>>     }
>>     prototype: { // TODO: does not have the right prototype
but you can explicitly set it using <|
>>         update(camera) {
>>             ...
>>             super(update);
>>         }
>>     }
>> };

This is essentially what I showed in the second code snippet of my original post and is a technique that I used to create the non-constructable  base AbstractClass in my collections experiment.  It works ok for situations where you need to break the parallelism of the class/instance inheritance hierarchies which you tend to want to do at the root of a hierarchy.  However, for most situation, even when the class is abstract, I think it is best to follow the basic class pattern I provided.

Allen


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110804/58da62d9/attachment-0001.html>


More information about the es-discuss mailing list