using Private name objects for declarative property definition.

Brendan Eich brendan at mozilla.com
Sat Jul 9 21:22:14 PDT 2011


On Jul 9, 2011, at 7:22 PM, Allen Wirfs-Brock wrote:

> On Jul 9, 2011, at 5:02 PM, Brendan Eich wrote:
> 
>> On Jul 9, 2011, at 4:32 PM, Allen Wirfs-Brock wrote:
>> 
>>> On Jul 9, 2011, at 1:43 PM, Brendan Eich wrote:
>> 
>>> ... However, if that isn't your concern (and my perspective is that in most cases it shouldn't be) you might as well use a public trademark property on instances in which case a direct test for that property is probably all you need.
>>> 
>>> However, I agree that best practice would be for such property to have no access side-effects, regardless of how they were are implemented.
>> 
>> I'm not sure which however wins. ;-)
>> 
>> We have Array.isArray, Proxy.isTrapping (soon to be Proxy.isProxy?), Function.prototype.isGenerator (could be Function.isGenerator -- note different signature, so don't want both -- Object.getPrototypeOf(Function) === Function.prototype). I think methods on built-ins are winning.
> 
> I think instances that are self identifying or self classify are best and should be the default approach. Whether via a method or data properties is a secondary issue. However, some times you need to use have a separate object (let's call it an oracle) that is responsibility for doing the identification/classification.  There are various reasons for using an oracle:
> 1) stratification -  Proxy.isProxy is an example
> 2) It is impossible or inconvenient to add the classification interface to the appropriate instance interface.  I think that was the reason for Array.isArray.  We were more afraid of adding it to Array.prototype than to Array.

That's too bad, since we added Function.prototype.isGenerator (which as I noted last time shows up on Function, of course!) without problem. It's Object.prototype that people can't extend, and then the warning about other built-ins is pre-ES5, out of lack of enumerable control, so for fear of breaking for-in loops.

Adding a non-enumerable Array.prototype method seems doable to me, if the name is clear and not commonly used.


> 3)  Externally imposed classification that is not cohesive with the rest of an object's behavior. For example, isSerializable is probably dependent upon a specific kind of serializer.  Their might even be multiple different kinds of serializers installed in a system.

Good point.


>>> In general yes.  Let's say a new kind of Crazy DOM node interfaces defines that it has a isCrazyNode property and also has semantics that requires the use of a Proxy to natively implement it in JS. In that case isCrazyNode needs to be implemented via the Proxy.
>>> 
>>> On the other hand, we don't want random objects to start exposing isProxy properties just because a Proxy was used to implement it.  That would be unnecessarily exposing implementation details.  I fall back o my standard test, can Proxies be used to implement ES Array instances.  If such instances implemented via Proxies had a visible isProxy property they would not be a accurate implemented on the ES spec. for array instances.
>> 
>> Right, so Proxy.isProxy -- on the built-in namespace object (like a standard module binding), not on each proxy instance.
>> 
>> But if you want isCrazyNode intercession via a Proxy, as you state above. For an Array emulation via Proxy, you'd have to monkey-patch Array.isArray.
> 
> Well, if you were actually using Proxy to implement Array, you would probably also be providing the Array implementation.

Not necessarily -- you might just want isArray to return true for your proxied fakes and the real deal. But yes, you would have to provide at least Array.isArray. With all the standard methods on Array.prototype generic, I believe you wouldn't have to replace all of Array.


> However, arguably Array.isArray really should have been Array.prototype.isArray.  We treated as a case 2 from above.  May we really didn't need to, but that's water over dam.  I don't think we should use it as precedent for more greenfield situations.

First thought: Crock sold us on reformed Number.is{NaN,Finite} along with new Numer.isInteger. We can "do both" and correct course for the long run, perhaps: Array.isArray and Array.prototype.isArray. But first we should settle the data property vs. method issue.


>>> If you buy that self-identify is better, then the question becomes data property or instance methods. To me that comes down to, if someone is going to have to code  if (typeof obj.isFoo == 'function' && obj.isFoo()) ... why not just say if (obj.isFoo)...
>> 
>> I agree with that style when detecting a method.
>> 
>> BTW, it leads to the obj.isFoo?() conditional call idea (along with ?.). We never reached consensus there.
> 
> But f the method implementation is simply going to be return true or return false why do yo need to call it at all?

The general case is an optional method, a notification hook call-out or some such.

You're right that for object detection the method call seems a waste. We could have made Function isGenerator a non-writable, non-configurable, and non-enumerable boolean-valued data property.

But, then it would be an instance property, which burns storage (the shape or hidden class sharing is not the issue, the true or false value slot is); or else an accessor on Function.prototype, which is about as costly as a method in implementations. Users testing it would not have to write (), your point. That wins. But otherwise it's a wash.


> Smalltalk code guidelines would general say (but I'll use JS syntax here instead of Smalltalk):
> 
> 
> obj.isFoo    //ok
> obj.constructor.isFoo(obj)  //less ok, unless there is a good reason
> Foo.isFoo(obj)   //even less good
> 
> In the first two you are only statically coupled to obj
> in the third you are statically coupled to both obj and Foo.

Right, and as you say there may be a reason for that (reasons 1 and 3; not sure about 2).


>>> Data property testing is a way to accomplish this without the monkey patching.  
>> 
>> You mean without all the stub ^false / return false methods polluting Object? That is true, but again I return to the precedent (however recent) we've set: Array.isArray. Class-side, not instance-side.
> 
> right.  And I think that Array.isArray was a special case exception rather than an exemplar for the future.

We should correct course in ES.next.

/be


More information about the es-discuss mailing list