Monkey patching constructors in builtin class hierarchies?

/#!/JoePea joe at trusktr.io
Tue Oct 24 19:08:51 UTC 2017


It's a generic thing to forever impact all future descendants of
Element, in theory. For example, a feature like "Element Behaviors" as
[described here](https://github.com/w3c/webcomponents/issues/662) (but
I'm going to close that one and open a more concise issue with a
working implementation).

The idea is that it would work on any type of element because it is
really generic (just like jQuery works on any type of element), so
patching Element seemed the best way to do it.
/#!/JoePea


On Tue, Oct 24, 2017 at 11:36 AM, Andrea Giammarchi
<andrea.giammarchi at gmail.com> wrote:
> are you patching things you know need patching or you want to pollute
> forever the future with your patch?
> Whatever you are doing should not compromise WebGLElement so it's good you
> are unable to patch Element, IMO
>
> On Tue, Oct 24, 2017 at 3:03 PM, /#!/JoePea <joe at trusktr.io> wrote:
>>
>> I'm going to use the accessor technique as it's the only way. Thanks!
>>
>> I was trying to patch `Element` because there's also `SVGElement`. I
>> suppose I can duplicate efforts and patch both `HTMLElement` and
>> `SVGElement`; that's not too difficult, but it is conceptually uglier
>> because for example, what if browsers come out with `MathElement`,
>> `WebGLElement`, etc.
>> /#!/JoePea
>>
>>
>> On Tue, Oct 24, 2017 at 10:55 AM, Michał Wadas <michalwadas at gmail.com>
>> wrote:
>> >> To "polyfill" an idea I have for DOM, to propose it later, etc.
>> >
>> > You can consider something like this depending on your needs:
>> > https://gist.github.com/Ginden/03004e52b3d331e236b5256e3b4c08ff
>> >
>> > On Tue, Oct 24, 2017 at 7:47 PM, /#!/JoePea <joe at trusktr.io> wrote:
>> >>
>> >> @Michał
>> >>
>> >> > But why do you need this?
>> >>
>> >> To "polyfill" an idea I have for DOM, to propose it later, etc.
>> >>
>> >> @Logan
>> >>
>> >> > Why not store your data separately in a WeakMap and rather than
>> >> > injecting properties onto existing objects? Then you can initialize
>> >> > the
>> >> > stored data on first access of the data you want.
>> >>
>> >> I thought about that, it seems like the only way, but was curious to
>> >> see about the the during-construction way if possible.
>> >>
>> >> @Andrea
>> >>
>> >> But this one doesn't work:
>> >>
>> >> ```js
>> >> window.Element = class extends Element {
>> >>   constructor() {
>> >>     super();
>> >>     console.log('hello darkness my old friend');
>> >>   }
>> >> };
>> >>
>> >> // then ...
>> >> class MyEl extends HTMLElement {}
>> >> customElements.define('my-el', MyEl);
>> >>
>> >> new MyEl;
>> >> ```
>> >>
>> >> /#!/JoePea
>> >>
>> >>
>> >> On Tue, Oct 24, 2017 at 10:36 AM, Andrea Giammarchi
>> >> <andrea.giammarchi at gmail.com> wrote:
>> >> > I know I'm going to regret this already, but since I've secretly
>> >> > played
>> >> > with
>> >> > polyfills, you can still do this:
>> >> >
>> >> > ```js
>> >> > window.HTMLElement = class extends HTMLElement {
>> >> >   constructor() {
>> >> >     super();
>> >> >     console.log('hello darkness my old friend');
>> >> >   }
>> >> > };
>> >> >
>> >> > // then ...
>> >> > class MyEl extends HTMLElement {}
>> >> > customElements.define('my-el', MyEl);
>> >> >
>> >> > new MyEl;
>> >> > ```
>> >> >
>> >> > Regards
>> >> >
>> >> >
>> >> >
>> >> > On Tue, Oct 24, 2017 at 2:32 PM, Logan Smyth <loganfsmyth at gmail.com>
>> >> > wrote:
>> >> >>
>> >> >> Given that these are constructors that you don't own, adding your
>> >> >> own
>> >> >> properties to them seems like an overall ugly approach to me. Why
>> >> >> not
>> >> >> store
>> >> >> your data separately in a WeakMap and rather than injecting
>> >> >> properties
>> >> >> onto
>> >> >> existing objects? Then you can initialize the stored data on first
>> >> >> access of
>> >> >> the data you want.
>> >> >>
>> >> >> On Tue, Oct 24, 2017 at 10:25 AM, /#!/JoePea <joe at trusktr.io> wrote:
>> >> >>>
>> >> >>> Well, I know I can set accessors on a prototype easily, but what I
>> >> >>> mean is, I need each instance of an `Element` to have exactly one
>> >> >>> instance of something right after construction during the same
>> >> >>> synchronous operation.
>> >> >>>
>> >> >>> For example, if we have an application with a bunch of pre-existing
>> >> >>> Elements that do not extend from any classes of mine, I'd like for
>> >> >>> the
>> >> >>> all to have `foo` properties, so that I can do this:
>> >> >>>
>> >> >>> ```js
>> >> >>> const el = new SomeCustomElementThatWasDefinedCreatedByMe
>> >> >>> console.log(el.foo) // it exists and is specific to the instance,
>> >> >>> not
>> >> >>> a prototype property
>> >> >>>
>> >> >>> // or
>> >> >>>
>> >> >>> const div = document.createElement('div')
>> >> >>> console.log(div.foo) // it exists and is specific to the instance,
>> >> >>> not
>> >> >>> a prototype property
>> >> >>> ```
>> >> >>>
>> >> >>> Can this be done?
>> >> >>> /#!/JoePea
>> >> >>>
>> >> >>>
>> >> >>> On Tue, Oct 24, 2017 at 10:19 AM, /#!/JoePea <joe at trusktr.io>
>> >> >>> wrote:
>> >> >>> >> This feels like a problem similar to
>> >> >>> >> https://esdiscuss.org/topic/block-scoped-prototype-extensions
>> >> >>> >
>> >> >>> > @Boris, even if it were scoped, how do we monkey patch a
>> >> >>> > *constructor*? By the way, for some reason your link to
>> >> >>> > `https://esdiscuss.org/topic/block-scoped-prototype-extensions`
>> >> >>> > posted
>> >> >>> > as `https://esdiscuss.org/topic/block` which is 404. If you can
>> >> >>> > edit
>> >> >>> > it it would help others not to stumble on a broken link.
>> >> >>> >
>> >> >>> >> if that would be possible, then everyone could just monkey patch
>> >> >>> >> Object, right?
>> >> >>> >
>> >> >>> > But everyone can monkey patch the entire class already, aside
>> >> >>> > from
>> >> >>> > the
>> >> >>> > constructor, by modifying the prototype. Obviously if someone
>> >> >>> > returns
>> >> >>> > something new from the constructor they might break everything,
>> >> >>> > but
>> >> >>> > it
>> >> >>> > will be completely obvious and people then won't do that. The
>> >> >>> > same
>> >> >>> > applies with methods and properties, it is super easy to break
>> >> >>> > entire
>> >> >>> > applications monkey patching methods.
>> >> >>> >
>> >> >>> > ---
>> >> >>> >
>> >> >>> > So suppose I want to "polyfill" a concept. For example, I want
>> >> >>> > all
>> >> >>> > elements to have a new "foo" accessor after they've been
>> >> >>> > constructed.
>> >> >>> > Or for example, suppose `HTMLElement.prototype.style` and
>> >> >>> > `SVGElement.prototype.style` didn't exist yet. How would I patch
>> >> >>> > those
>> >> >>> > in?
>> >> >>> > /#!/JoePea
>> >> >>> >
>> >> >>> >
>> >> >>> > On Tue, Oct 24, 2017 at 10:07 AM, Michał Wadas
>> >> >>> > <michalwadas at gmail.com>
>> >> >>> > wrote:
>> >> >>> >> AFAIR DOM classes are not extensible by any means.
>> >> >>> >>
>> >> >>> >>
>> >> >>> >>
>> >> >>> >> On 24 Oct 2017 6:51 pm, "/#!/JoePea" <joe at trusktr.io> wrote:
>> >> >>> >>>
>> >> >>> >>> Is it possible to monkey-patch an intermediate constructor of a
>> >> >>> >>> built-in
>> >> >>> >>> subclass?
>> >> >>> >>>
>> >> >>> >>> For example, suppose I want all `Element` instances in a web
>> >> >>> >>> app
>> >> >>> >>> to
>> >> >>> >>> have
>> >> >>> >>> new instance properties, is there a way to monkey-patch the
>> >> >>> >>> Element
>> >> >>> >>> constructor so that when I make a custom element by extending a
>> >> >>> >>> subclass of
>> >> >>> >>> `Element` that the new logic will fire?
>> >> >>> >>>
>> >> >>> >>> For example:
>> >> >>> >>>
>> >> >>> >>> ```js
>> >> >>> >>> // monkey-patch the Element constructor somehow so that it logs
>> >> >>> >>> "patched
>> >> >>> >>> in Element".
>> >> >>> >>>
>> >> >>> >>> // then
>> >> >>> >>> class FooBar extends HTMLElement {}
>> >> >>> >>> customElement.define('foo-bar', FooBar)
>> >> >>> >>> new FooBar // "patched in Element"
>> >> >>> >>> ```
>> >> >>> >>>
>> >> >>> >>> I tried
>> >> >>> >>>
>> >> >>> >>> ```js
>> >> >>> >>> const OldElement = window.Element
>> >> >>> >>>
>> >> >>> >>> window.Element = function Element() {
>> >> >>> >>>   const _this = new OldElement
>> >> >>> >>>   console.log("patched in Element")
>> >> >>> >>>   return _this
>> >> >>> >>> }
>> >> >>> >>>
>> >> >>> >>> window.Element.prototype = OldElement.prototype
>> >> >>> >>> window.Element.prototype.constructor = window.Element
>> >> >>> >>>
>> >> >>> >>> class FooBar extends HTMLElement {}
>> >> >>> >>> customElements.define('f-b', FooBar)
>> >> >>> >>> new FooBar // does not log "patched in Element"
>> >> >>> >>> ```
>> >> >>> >>>
>> >> >>> >>> But when I make a new custom element, constructing it seems to
>> >> >>> >>> use
>> >> >>> >>> the old
>> >> >>> >>> Element constructor, as if a non-global reference to the
>> >> >>> >>> original
>> >> >>> >>> constructor is kept inside a module so that modifying the
>> >> >>> >>> global
>> >> >>> >>> wouldn't
>> >> >>> >>> have any effect.
>> >> >>> >>>
>> >> >>> >>> Is there a way to monkey patch a constructor in the middle of a
>> >> >>> >>> built-in
>> >> >>> >>> prototype chain or to otherwise inject construction logic to
>> >> >>> >>> base
>> >> >>> >>> classes of
>> >> >>> >>> existing class hierarchies?
>> >> >>> >>>
>> >> >>> >>>
>> >> >>> >>> /#!/JoePea
>> >> >>> >>>
>> >> >>> >>> _______________________________________________
>> >> >>> >>> es-discuss mailing list
>> >> >>> >>> es-discuss at mozilla.org
>> >> >>> >>> https://mail.mozilla.org/listinfo/es-discuss
>> >> >>> >>>
>> >> >>> >>
>> >> >>> _______________________________________________
>> >> >>> es-discuss mailing list
>> >> >>> es-discuss at mozilla.org
>> >> >>> https://mail.mozilla.org/listinfo/es-discuss
>> >> >>
>> >> >>
>> >> >>
>> >> >> _______________________________________________
>> >> >> es-discuss mailing list
>> >> >> es-discuss at mozilla.org
>> >> >> https://mail.mozilla.org/listinfo/es-discuss
>> >> >>
>> >> >
>> >> _______________________________________________
>> >> es-discuss mailing list
>> >> es-discuss at mozilla.org
>> >> https://mail.mozilla.org/listinfo/es-discuss
>> >
>> >
>
>


More information about the es-discuss mailing list