Constructors need to be able to recognize uninitalized instances(?)
Thanks!
awbjs IOW, I’d write the check: if (typeof this !== "object" || !($fooBrand in this) || this[$fooBrand]) /CAAF/
perhaps: if ( this ===null || typeof this !== "object" || this[$fooBrand]!==undefined) /CAAF/ if depends upon how the @@create method chooses to use as an "uninitialized" marker. Setting the brand property value to undefined works in many cases.
The this[$fooBrand]!==undefined
still looks wrong (it would be true for an uninitialized instance). How about the following?
if ( this ===null || typeof this !== "object" || this[$fooBrand] !== false) /*CAAF*/
Then the last Or operand is true if either the instance has already been initialized or if this
is an object that does not have a property whose key is $fooBrand (e.g. a namespace object). Interestingly, this expression is not the same as ! this[$fooBrand]
.
@awbjs On slide 22, wouldn’t you first call @@create (⇒this[$fooBrand]=false) and then execute the “constructor initialization case”?
Are you talking about for the CAAF case? If you want factory function behavior I would code the then clause as: return new Foo(); or perhaps: return new this.constructor() You could put in explicit calls to @@create but it probably better to let new do that part.
In the Rev 14 draft, most of the built-in constructors that have CAAF behavior use some variation of this pattern expressed in pseudo code.
I think the built-in support in slides 23 and 24 would be best, but TC39 doesn't really understand it yet.
I’d prefer a dynamic solution, but don’t see one.
if ( this ===null || typeof this !== "object" || this[$fooBrand] !== false) /*CAAF*/
Then the last Or operand is true if either the instance has already been initialized or if
this
is an object that does not have a property whose key is $fooBrand (e.g. a namespace object). Interestingly, this expression is not the same as! this[$fooBrand]
.
Sorry, correction:
this[$fooBrand] !== false
is not the same as this[$fooBrand]
. The former is true for undefined
, the latter isn’t. The following is more intention-revealing:
if ( this ===null || typeof this !== "object" || this[$fooBrand] === undefined || this[$fooBrand]) /*CAAF*/
I think the idea of detecting if you have initialized or noninitialized instance at runtime is very unhappy idea.
You know when it is called as an initializer (2, 4 below) and when not (1 below). 3 below is legacy way of doing super - it can be simply replaced.
And there is also no breaking compatibility cost - so far, there was no API to distinguish this; so once it is added, it can be safely specified that "initializer" context is only 2 and 4.
[Referring to Allen’s slides: meetings:subclassing_builtins.pdf ]
Is this really true?
I can see four ways of invoking a constructor function C:
new
: new C(...)call
: C.call(this, ...)super
, in a sub-instance, as a method (similar to #3): super.constructor(...)C[@@create] is only invoked during #2 (by the
new
operator). Thus, the constructor’s role is always: set up an uninitialized instance. You could add a check against an instance being initialized twice, but that doesn’t seem to be what the slides are about.The slides mention one use case: What if you want to have a function Foo that can be either called as a function or as a constructor? You can’t detect the difference if Foo is inside a namespace object. That is, you want to distinguish #1 from all others, but #1 could happen to a namespaced C. Observations:
– Does recognizing whether
this
is initialized really help here? Isn’t it more about checking whetherthis
is an instance of C?– I’ve always considered this to be more of an anti-pattern for non-builtin code. Thus, it seems useful to support a check, but only so that namespaced constructors can avoid accidentally polluting the namespace object.
– Won’t namespace objects go away with modules?
Thanks!
Axel