W3C home > Mailing lists > Public > public-webapps@w3.org > January to March 2015

RE: ES6 and upgrading custom elements

From: Domenic Denicola <d@domenic.me>
Date: Wed, 7 Jan 2015 04:53:47 +0000
To: Dimitri Glazkov <dglazkov@google.com>, Anne van Kesteren <annevk@annevk.nl>
CC: WebApps WG <public-webapps@w3.org>
Message-ID: <CY1PR0501MB1369B85A3CF4268C089E3218DF460@CY1PR0501MB1369.namprd05.prod.outlook.com>
This is all intimately tied to the still-ongoing how-to-subclass-builtins discussion that is unfortunately happening on a private TC39 thread. The essential idea, however, is that as long as you do

class MyInputElement extends HTMLInputElement {
  constructor() {
    super(); // this is key

then all instances of MyInputElement will get all internal slots and other exotic behavior of HTMLInputElement. The `super()` call is the signal that the object should be allocated and initialized using the logic of HTMLInputElement instead of Function.prototype (the default).

This will require defining a constructor for HTMLInputElement, of course, but it's pretty predictable you wouldn't be able to create subclasses of something without a constructor.

I am not sure exactly how this fits in with an ES6-era document.registerElement. This is the right time to start thinking about that, certainly.

Off the top of my head, ideally document.registerElement should just be a "registration" call, and should not actually need to modify the constructor or create a new constructor as it does today. The generated constructor in [1] does nothing special that the above constructor (which consists entirely of `super()`) could not be made to do automatically, as long as the Element constructor was made smart enough. (`super()` eventually will delegate up to the Element constructor.)

To explain that in more detail I need one more probable-ES6 concept, which is the "new target." In the expression `var x = new MyInputElement()`, ` MyInputElement` is the "new target". Part of the ES6 subclassing story is that all superclass constructors will be passed an implicit parameter for the new target. So, when the `MyInputElement()` constructor calls `super()`, that calls the [[Construct]] internal method of HTMLInputElement, passing in the new target of MyInputElement. Similarly when HTMLInputElement in turn calls `super()`, that will call the [[Construct]] internal method of HTMLElement, still passing in the new target of MyInputElement. Etc. until Element also gets a new target of MyInputElement.

With this in hand we could see how to define a "smart enough" element constructor. It would first look up the new target in the custom element registry. If it is there, it retrieves the definition for it, and can do all the work currently specified in [1]. That means that the actual constructor for `MyInputElement` can be as above, i.e. simply `super()`.

[1]: https://w3c.github.io/webcomponents/spec/custom/#dfn-custom-element-constructor-generation

> Conceptually, when I wrote it I'd imagined that the constructor will be called only when you explicitly invoke it (new FooElement...). When parsing or upgrading, the constructor would not be called. The createdCallback will be invoked in either case.

I do not think this makes any sense. All instances of a class can only be created through its constructor. createdCallback should be left behind as legacy IMO.


With all this in mind I do not think "extends" is necessary. I am less sure about this though so take what follows with a grain of salt. But anyway, IMO you should just look at the inheritance chain for the constructor passed in to registerElement.

Also there is a pretty simple hack if you still want extends. Which is that the HTML standard should just define that `HTMLInputElement.extends = "input"`. Then in Dmitry's

class X extends HTMLInputElement { .. }
X.extends = "input" // additional line.
document.register("x-input", X)
var xinput = new X

the need for the additional line disappears since `X.extends === HTMLInputElement.extends === "input"`.

(I guess this breaks for classes that have multiple tag names :-/. But it is pretty weird that I can't define `class QQ extends HTMLQuoteElement {}` and then do both `<q is="x-qq">` and `<blockquote is="x-qq">`. You would think I could do both. More reason to dislike `extends`.)

Received on Wednesday, 7 January 2015 04:54:16 UTC

This archive was generated by hypermail 2.3.1 : Friday, 27 October 2017 07:27:25 UTC