Re: [w3c/webcomponents] Non-class based example of customElement.define() (#587)

@rniwa just sent me to this issue. I'd like to share some of what we've done on the polyfill side of things...

First, we have a "native shim" to the Custom Elements polyfill so that ES5 constructors can be used to implement elements. There have been two versions of this shim:

The first version patched `window.HTMLElement` as a constructor function that used `Reflect.construct` along with `this.constructor` to emulate `new.target`. This has some prohibitive performance issues because 1) `Reflect.construct` is slow and 2) `Reflect.construct` isn't a real substitute for `super()` as it always creates a new instance, so this new `HTMLElement` constructor would always throw away the currently initializing instance and return a new Element instance. (old version: https://github.com/webcomponents/custom-elements/blob/b43236a7da0917ea938b6cb1aa3116caaeb6e151/src/native-shim.js )

The new version patches up the CustomElementRegistry API to generate a stand-in class at `define()` time and define that, and then keep it's own registry of user-defined ES5 constructors. It then does some shuffling for initialization. This approach is much faster and incurs only a 10% overhead over native CEs. The new version is here: https://github.com/webcomponents/custom-elements/blob/master/src/native-shim.js

There are some caveats that I list in the comments of the shim:

  1. All constructors in a inheritance hierarchy must be ES5-style, so that they can be called with Function.call(). This effectively means that the whole application must be compiled to ES5.
  2. Constructors must return the value of the emulated super() call. Like `return SuperClass.call(this)`
  3. The `this` reference should not be used before the emulated super() call just like `this` is illegal to use before super() in ES6.
  4. Constructors should not create other custom elements before the emulated super() call. This is the same restriction as with native custom elements.

1) is a restriction because ES5 constructors cannot emulate `super()` and call into an ES6 constructor. 2) is just making ES5 constructors slightly more spec-compliant with ES6 constructors and required because HTMLElement sometimes returns an object other than `this`. I've worked with the major compilers to get their class transformations to implement this properly. Babel already worked. TypeScript has just fixed this, and Closure's fix is in review now. 3) is just respected the TDZ for `this` even in ES5 constructors. This shouldn't be something that authors need to care about if they write ES6. 4) is the same restriction that native CEs have.

What this means for Custom Elements authors is that everyone should write and distribute ES6 classes and let applications do any compiling down to ES5 that they need. This is a little different than the current norm of writing in ES6 and distributing ES5, but it will be necessary for any libraries that extend built-ins - Custom Elements aren't really unique here. Apps can either send ES5 to older browsers and ES6 to newer browser, or ES5 to everything using the shim.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/webcomponents/issues/587#issuecomment-259775926

Received on Thursday, 10 November 2016 18:58:41 UTC