Finding a "safety syntax" for classes

Allen Wirfs-Brock allen at wirfs-brock.com
Tue Mar 27 22:30:26 PDT 2012


On Mar 27, 2012, at 11:59 AM, Brendan Eich wrote:

> Russell Leggett wrote:
>> Can we make it writable:false, configurable:true? Of course, that would only be a minor deterrent from changing it around, but maybe just enough?
> 
> This has zero integrity, so is IMHO worse than worthless -- it's misleading. Not that anyone will count on it, but stopping assignments while allowing reconfiguration or deletion and redefinition does not smell right.

I think {writeable: false, configurable: true} is, in general,  a great attribute combination.  That's why I keep spec'ing things to use it.  It isn't about integrity guarantees.  It's about reinforcing the intended normal application level usage intent of a feature while preserving the ability perform reflective metaprogramming. It is about more clearly stratify the application and meta levels of ES.  It's about = keeping straight which strata = belongs in.

JavaScript has always (I assume) allowed = to be used to create properties.

var obj = {};
obj.x = 42;
obj.y = functionI) {return this.x*2};

The problem with such assignments is there is no clue whether x and y intended to used fixed data members of the object, expando data members, dynamic keys of a hash map data structure, or in the case of y is it a method or simply a data element with a first class function value.  y even shows up in for-in data element enumerations (we infer from array behavior that for-in was intended to iterate over the data elements of an object and not the behavioral elements (eg methods).

But if I use concise method definitions in object literals or class declarations (or Object.defineMethod) I'm making a stronger statement about function valued properties.  I'm explicitly saying that these properties are to be treated as behavioral elements not as data elements.  Setting the attributes of concisely defined methods to {enumerable: false, writable:false, configurable: true} helps me guide the users of abstraction into usage patterns.

By setting writable: false we are saying, don't use the assignment operator to modify the value of this property. It isn't data. If you try to, the assignment will be ignored (or throw in strict mode).  This applies whether you are directly accessing such a method on an object created by an object literal or operating upon an instance that is inheriting the property from a prototype.   However, it does "freeze" the method.  If you need to do meta programing involving such methods you still can.  You just have to use Object.defineProperty/defineMethods. You have to act like you know what you are doing if you want to manipulate a property that has been explicitly declared as a method.


> 
> To answer Dave's question, I meant that classes without any extra Object.defineProperty calls can be used to implement built-ins. Not a huge win but seemingly better than requiring Object.defineProperty for that case -- assuming class declarations create a constructor property on the class prototype.
Chapter 15 built-ins have prototype.constructor that are writable and configurable.  It is the prototype property that is non-writable and non-configurable for built-in constructors.


> 
> If we want class C {} to create no C.prototype.constructor back-link to C, then we're not sugaring the prototypal pattern. That seems like too much innovation instead of desugaring, but you could model it via delete C.prototype.constructor after class C{} assuming constructor is configurable.

In all the discussion about deleting (or not having) a "constructor" property on the prototype, I think people are over-looking (I haven't seen it mentioned) that if a subclass is going to do a super constructor call (whether the name "constructor" is syntactically explicit or implicit) then their better damn well be a property named "constructor" somewhere above it on the [[Prototype]] chain. 

> 
> Of course one could argue based on desugaring to user-defined (not built-in) functions that C.prototype.constructor should be configurable and writable:

This is actually how the the maximally minimal class strawman currently defines it and for exactly the reason that it is what function() {} does.  However, this thread has actually convinced me that it probably should be configurable and non-writable (and, of course, non-enumerable).

We shouldn't want to see this:

class C () {
   ...  //methods
}
C.prototype.constructor = null;

if somebody has a good enough reason for this they should mean it enough to say 

Object.defineProperty(C.prototype,"constructor",{value: null});


Allen


More information about the es-discuss mailing list