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

/#!/JoePea joe at trusktr.io
Thu Jul 28 02:51:59 UTC 2016


> 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/726b3a9e/attachment-0001.html>


More information about the es-discuss mailing list