From: Travis Leithead [mailto:travis.leith...@microsoft.com] 

> Something magical happens here. The use of super() is supposed to call the 
> constructor of the HTMLElement class—but that’s not a normal JS class. It 
> doesn’t have a defined constructor() method [yet?].

Yep. We'd need to define one; it's absolutely required. ES2015 classes that 
don't call super() in their constructor simply aren't allowed, which means 
inheriting from a constructor that throws (like HTMLElement currently does) is 
impossible.

https://github.com/domenic/element-constructors is a start at this.

> I’m trying to rationalize the custom elements previous design with the use of 
> constructors. Basically, I think it should still be a two-stage creation 
> model:
> 1. Native [internal] element is created with appropriate tag name, 
> attributes, etc.
> 2. JS constructor is called and provided the instance (as ‘this’)
>
> #1 is triggered by the parser, or by a native constructor function. That 
> constructor function could either be provided separately like it is returned 
> from registerElement today, or in some other way (replacing the original 
> constructor?). Since replacing the original constructor sounds weird and 
> probably violates a bunch of JS invariants, I’ll assume sticking with the 
> original model. 
>
> This makes it much safer for implementations, since the native custom element 
> can always be safely created first, before running JS code. It also means 
> there’s no magic super() at work—which seems to leave too much control up to 
> author code to get right.

Ah! This is a big mistake!! You simply cannot do this split with ES2015 classes.

ES2015 classes are predicated on the idea that construction is an atomic 
operation, consisting of both allocation and initialization in one step. As 
such, class constructors cannot be called, only `new`ed. You cannot apply them 
with an already-created `this`, as if they were methods. This is fundamental to 
the class design; any other model needs to fall back to simply functions, not 
classes.

Indeed, the currently-specced design had a two-stage model---allocation/base 
initialization by the UA-generated constructor returned by 
document.registerElement, and author-controlled initialization by 
createdCallback(). This separation is, as you point out, makes things safer and 
easier to specify. And it is absolutely impossible to achieve if you insist on 
custom constructors, because in that case all allocation and initialization 
must happen together atomically. If we allow custom constructors, the 
allocation and initialization must both happen with a synchronous call to the 
custom constructor (which itself must call super()).

I take it when you said in [1]:

> I've discussed this issue with some of Edge's key parser developers. From a 
> technical ground, we do not have a problem with stopping the parser to 
> callout to author code in order to run a constructor, either during parsing 
> or cloning. For example, in parsing, I would expect that the callout happens 
> after initial instance creation, but before the target node is attached to 
> the DOM tree by the parser.

you were not aware of this? Maybe now you better understand my follow-up 
question in [2],

> Can you expand on this more? In particular I am confused on how "initial 
> instance creation" can happen without calling the constructor.

[1]: https://lists.w3.org/Archives/Public/public-webapps/2015JulSep/0161.html
[2]: https://lists.w3.org/Archives/Public/public-webapps/2015JulSep/0162.html

> Basic example of what I’m thinking:
>
> class XFooStartup extends HTMLElement {
>   constructor(val1, val2) {
>      this.prop = val1;
>      this.prop2 = val2;

This is invalid at runtime, since it fails to call super().

>   }
> }
> window.XFoo = document.registerElement(‘x-foo’, XFooStartup);

Why is XFoo different from XFooStartup? If I define a method in XFooStartup, 
does it exist in XFoo?

> // (1)
> var x1 = new XFooStartup(“first”, “second”);
> // (2)
> var x2 = new XFoo(“first”, “second”);
>
> Calling (1) does not create a custom element. Extending from HTMLElement is 
> not magical, it’s just a prototype inheritance, as can be done today. super() 
> would do whatever super() does when the super class has no defined method to 
> invoke.

(It would throw, in other words.)

> x1 is a regular JS object with a .prop and .prop2.

This can't be true. Either x1 doesn't exist, because you didn't call super() 
and so `new XFooStartup` threw. Or x1 called super(), and it is no longer an 
ordinary JS object, because by calling super() (which is basically shorthand 
for `this = new super()`) you have made sure that it is a true 
allocated-and-initialized-by-the-UA HTMLElement.

> Calling (2) runs the platform-provided constructor function which internally 
> inits a new HTMLElement in the C++ (or could be Element if in XML document?). 
> Then the platform immediately (synchronously) invokes the provided 
> constructor function as if:
XFooStartup.apply(newHTMLElementInstance, argsPassedThroughFromCall);
> so that XFooStartup’s constructor operates on the ‘this’ being the instance 
> created by the platform.

Since XFooStartup is a class, `XFooStartup.apply` will throw an error.

This also doesn't address how any methods defined in the XFooStartup class may 
or may not get over to XFoo.

Hope this clarifies things! I had a suspicion there was some fundamental 
misunderstanding here...

Reply via email to