The `super` keyword doesn't work as it should?

/#!/JoePea joe at trusktr.io
Thu Jul 28 03:16:48 UTC 2016


For now, I've settled with writing classes like this:

```js
const SomeClassMixin = base => {
    class SomeClass extends base {
        // ...
    }

    return SomeClass
}

const SomeClass = SomeClassMixin(class{})
SomeClass.mixin = SomeClassMixin

export {SomeClass as default}
```

This makes it possible for an end user to import the class and extend from
it like normal:

```js
import SomeClass from './SomeClass'

class OtherClass extends SomeClass {
    // ...
}
```

But, it also allows and end user to use it as a mixin:

```js
import SomeClass from './SomeClass'
import AnotherClass from './AnotherClass'

class OtherClass extends SomeClass.mixin(AnotherClass) {
    // ...
}
```

This has two main downsides:

- The class creator has to write the class as a class-factory mixin, then
remember to export the mixin application on a plain `class {}`
- It is not possible to pass specific arguments to the constructors of each
class.

One of my implementations of `multiple()` *does* allow one to pass
arguments to each constructor. I'll re-visit that when Proxy is supported
in Safari.

I can't think of any way to make this work using a class helper library,
because those need to create prototype chains, and thus `super` cannot be
modified unless using `eval`, in which case original scope is lost.

I literally think that a dynamic `super` and/or
`Function.prototype.toMethod` would allow me to implement the ideal
solution, which would have the following benefits:

I am going to leave this alone for now, and just continue with the
class-factory mixin approach, although that is not my ideal solution.

Any ideas or suggestions?

*/#!/*JoePea

On Wed, Jul 27, 2016 at 7:51 PM, /#!/JoePea <joe at trusktr.io> wrote:

> > 1) There are indeed use cases for dynamically configuring the
> HomeObject binding but they are quite specialized
>
> I suppose my case is quite specialized. I am wanting to duplicate the
> prototype chains of ES6 classes, but I have no reliable (afaik) way to make
> the `super` keyword work on copied methods. If I copy the method by
> reference then the `super` keyword is based on the wrong `HomeObject`. If I
> copy the method by getting its the source with `.toString()` followed by
> using `eval` to define the new method on an object initialization, that
> works and `super` will work, but this has problems if the original method
> relied on variables in the outer scope where it was originally defined as
> the copied method will not have access to that scope (f.e. code transpiled
> by Babel may not be able to see the Babel helper functions).
>
> I am trying to implement a function `multiple()` so that I can do
>
> ```js
> class SomeClass { ... }
> class
> ​OtherClass​
>  { ... }
> class Foo extends multiple(SomeClass, OtherClass) { ... }
> ```
>
> and it's proving to be really difficult because I can't configure
> `HomeObject`. The `multiple` function copies the prototype chains of each
> class, then combines them together (when possible, otherwise an error is
> thrown, depending on the native prototypes involved). As mentioned, this
> somewhat works using `eval()` in the implementation, except that copied
> methods don't work if they rely on variables of the outer scope where they
> were originally defined. For example, suppose `SomeClass` in the
> following example was imported from another file, and it depends on Babel
> helpers defined in the original scope where it was imported from:
>
> ```js
> import SomeClass from './somewhere/SomeClass'
> class
> ​OtherClass​
>  { ... }
> class Foo extends multiple(SomeClass, OtherClass) { ... }
> ```
>
> The `multiple()` call will copy the methods of `SomeClass` onto a new
> prototype chain (the methods need new `HomeObjects`), but the new methods
> will fail to reference anything from the original scope where `SomeClass`
> came from.
>
> I need to be able to modify `HomeObject`s without using an `eval()` hack
> in order for my `multiple()` implementation to work 100% as intended.
>
> For now I may need to abandon my effort and use class-factory "mixins" as
> in
>
> ```js
> let SomeMixin = baseClass => class SomeMixin extends baseClass { ... }
> ```
>
> , but the downside of this is that the classes are not usable as
> standalone classes, so and end-user can't do
>
> ```js
> class Foo extends SomeClass { ... }
> ```
>
> We are required to do
>
> ```js
> class Foo extends SomeClass(null) { ... }
> ```
>
>  but due to the `null` the class we won't have `Object` methods. We could
> do
>
> ```js
> class Foo extends SomeClass(Object) { ... }
> ```
>
> , but that is not also ideal as `Object` is not guaranteed to be the
> native Object; `window.Object` could be overridden.
>
> I really want to define plain ES6 classes and use 3rd party plain ES6
> classes (not class factories). It means that those classes are not limited
> to being used as mixins.
>
> For reference, here's the implementation so far (with the ugly caveats of
> cloning methods with `eval`):
> https://gist.github.com/trusktr/8c515f7bd7436e09a4baa7a63cd7cc37
>
> I was planning to add caching so that combinations of classes could be
> re-used (similar to what [mixwith.js](
> https://github.com/justinfagnani/mixwith.js) does to cache mixin
> applications).
>
> > We aren’t going to do anything until there is significant real world
> feedback saying that it really is needed.
>
> Here's that feedback. :D
>
> TLDR, it seems that without flexibility in modifying `[[HomeObject]]` in
> order to control what `super` references, I cannot implement an ideal
> `multiple()`-inheritance helper that I would be able to comfortably suggest
> for use in a production app. I would definitely not recommend the my
> `eval`-based implementation as it will fail in ways that are very
> undesirable and unexpected.
>
> With the limitation of a static `super`, I can't do something simple like
> use `Object.assign` to simply copy some methods from one class' prototype
> onto the class where I need them. That's normal pre-ES6 stuff that has
> worked along with a dynamic `this` since forever, so the change in behavior
> from `this` to `super` doesn't fit with the pre-ES6 language that we are
> previously accustomed to and that we love (I do). ES6+ is
> backwards-incompatible with some pre-ES6 paradigms.
>
> Pleeeease, let's add something like `toMethod`, or make `super` dynamic
> (preferably both). I don't see any downside to having something like
> `Function.prototype.toMethod`, as it is an opt-in type of feature, so it
> won't bring with it performance loss like a dynamic `super` might (I still
> haven't heard conclusive evidence regarding those performance losses).
>
> From what I can imagine, property lookup checks checks the current object,
> then goes to the next prototype and does the same, until finally it reaches
> a prototype where the property is found. Once that object (a HomeObject) is
> found, we know what the `HomeObject` is. It seems very easy to call a found
> method with that found `HomeObject`argument, and when the method is called
> and if it uses `super`, then a similar search will happen up the prototype
> chain until the matching super property is found. The second prototype
> search (the one initiated due to the method using `super`) is already a
> requirement, so, this means that the extra overhead for a dynamic `super`
> stems from simply passing as argument the `HomeObject` of a method once the
> method is found on a prototype chain. A static super means that the
> `[[HomeObject]]` variable is simply defined forever instead of  being
> marked in the property-lookup of a method, and that doesn't seem like tons
> of extra overhead. Note that the property lookup is going to happen
> anyways, so why not mark the `HomeObject` during the property lookup
> algorithm?
>
> *Is that really enough overhead to merit a static `super`?* If someone can
> explain it (or link to it), that would be greatly appreciated.
>
> I also think having dynamic `super` (or at least a way to configure it)
> opens up language possibilities that can make interesting things happen
> (for example, my `multiple()`-inheritance implementation would work
> smoothly and instanceof checks would also work thanks to `@@hasInstance`).
> The mere fact that a dynamic or configurable `[[HomeObject]]` would allow
> for the creation of a tool like what I am trying to implement is good
> enough reason to have the feature. Who knows what other awesome ideas
> people will come up with.
>
> */#!/*JoePea
>
> On Sun, Jul 24, 2016 at 7:54 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>
> wrote:
>
>>
>> On Jul 24, 2016, at 7:04 PM, /#!/JoePea <joe at trusktr.io> wrote:
>>
>> What if there was also something like `Function.prototype.bind` like
>> `Function.prototype.with`, so `someFunc.with(homeObject)` returns a new
>> function who's [[HomeObject]] is the specified `homeObject`. It would be
>> possible to do `someFunc.with(...).bind(...)` to configure both the home
>> object and `this`.
>>
>>
>> This was  included in the ES6 drafst for quite awhile. Initially with the
>> name defineMethod and latter as toMethod.  But it was eventually decided to
>> remove it.  The pros and cons of such a function were extensively discussed
>> with in TC39 over a several year period. For example,  see
>> https://github.com/tc39/tc39-notes/blob/master/es6/2014-01/jan-28.md#more-on-tomethod
>>
>>
>> The issues with defineMethod/toMethod are summarized in
>> https://github.com/tc39/tc39-notes/blob/master/es6/2015-03/mixin-proposal.pdf
>>  and https://github.com/allenwb/ESideas/blob/master/dcltomethod.md which
>> is a proposal for an alternative way to address the problem. Notes from
>> that discussion are at
>> https://github.com/tc39/tc39-notes/blob/master/es6/2015-03/mar-25.md#6iv-a-declarative-alternative-to-tomethod-allen-wirfs-brock
>>
>>
>> Where it has been left by TC30 is roughly:
>> 1) There are indeed use cases for dynamically configuring the HomeObject
>> binding but they are quite specialized.
>> 2) All of the solution that have been proposed are confusing or error
>> prone.
>> 3) So far, the actual user demand for such a feature is small.
>> 4) We aren’t going to do anything until there is significant real world
>> feedback saying that it really is needed.
>>
>> Allen
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160727/f1d4d205/attachment-0001.html>


More information about the es-discuss mailing list