Accesssing ES6 class constructor function

James Treworgy jamietre at gmail.com
Thu Jan 5 19:21:24 UTC 2017


> Can you clarify what prevents it from being made to work?

The fundamental feature difference (continuing to think about this!) is
that with ES5 constructors, I can create an instance of something from an
abitrary constructor provided to me, and inject properties that will be
available to it *at contruction time.* The basic operation of the container
might be like this

```js
function createInstance(Cotr, args /* array */) {
    function F() {
       // this is dynamic in reality but simple example of injecting
something...
       this.logger = new Logger();

       var instance = Cotr.apply(this, args)
       return instance; // in case Cotr returns something
    }
    F.prototype = Cotr.prototype;
    return new F();
}
```

So the Cotr can refer to "this.logger" in the constructor. I don't think
there's a way to do this with dynamic class inheritance since you always
have to call super() before you can assign any properties in a constructor.

This isn't a dealkiller for the tool overall - I can constructor injection
instead:

```js
class Thing
    static _inject = [Logger]
    constructor(deps, ...args) {
        // deps = { logger: Logger, dep2: Dep2, ... }
        Object.assign(this, deps) // or however you want to make them
available
    }
}
```
This will work fine with the dynamic subclass pattern. it just was nice to
have everything done by the framework and get rid of boilerplate, but this
also has benefits of classes working without the DI container. :) I'm
bringing this into an ES6 project for the first time so I can live with a
different pattern.


On Thu, Jan 5, 2017 at 1:55 PM, T.J. Crowder <
tj.crowder at farsightsoftware.com> wrote:

> On Thu, Jan 5, 2017 at 5:31 PM, James Treworgy <jamietre at gmail.com> wrote:
>
> I can't address your questions about "why" (I wasn't plugged into the
> discussions around it), but addressing this:
>
> > This has come into play lately for me, as an DI container we use that
> > does exactly this doesn't work with ES6 classes (and as far as I can
> > tell, there's no way to make it work, other than having devs no longer
> > use class syntax).
>
> Can you clarify what prevents it from being made to work? I'm probably
> missing the point you're making there. For instance, this does some
> brain-dead DI (injecting an argument in the constructor) by dynamically
> extending the class:
>
> ```js
> // The class we'll do DI on
> class Original {
>     constructor($foo) {
>         this.foo = $foo;
>     }
>     run(num) {
>         const result = this.foo.fooMethod(num);
>         console.log(`num is ${num}, result is ${result}`);
>     }
> }
>
> // Brain-dead di function
> const di = (cls, Foo) => {
>     const o = {
>         [cls.name]: class extends cls {
>             constructor(...args) {
>                 super(new Foo(), ...args);
>             }
>         }
>     };
>     return o[cls.name];
> };
>
> // Ues a class that's been DI'd
> const use = Original => {
>     new Original().run(42);
> };
>
> // Use it in dev
> use(di(Original, class Foo {
>     fooMethod(num) {
>         return num * 2;
>     }
> }));
>
> // Use it in production
> use(di(Original, class Foo {
>     fooMethod(num) {
>         return num / 2;
>     }
> }));
> ```
>
> That outputs
>
> num is 42, result is 84
>
> num is 42, result is 21
>
> ...because of the different injected `Foo`s. (This is obviously a
> simplistic example.)
>
> Separately, there are some tools you can use, such as
> [`Reflect.construct`][1], but granted that does create an instance. For
> instance, if for some reason you wanted to extend a class *without* using
> `class`:
>
> ```js
> class A {
>     amethod() {
>         console.log("amethod");
>     }
> }
>
> function B() {
>     const t = Reflect.construct(A, [], B);
>     return t;
> }
> B.prototype = Object.create(A.prototype);
> B.prototype.constructor = B;
>
> B.prototype.bmethod = function() {
>     console.log("bmethod");
> };
>
> const b = new B();
> b.amethod();                 // "amethod"
> b.bmethod();                 // "bmethod"
> console.log(b instanceof A); // true
> console.log(b instanceof B); // true
> ```
>
> Of course, that cheats a bit with that `return t;`. :-)
>
> There are probably some tools that should be added to the list. For
> instance, there's [this proposal][2] for `Reflect.isCallable` and
> `Reflect.isConstructor`). And my `bmethod` above isn't really a method, so
> it wouldn't be able to use `super`; in theory one could argue for a
> `Reflect.makeMethod` (but use cases are limited, given `class` syntax). New
> tools can be added if persuasive use cases come up (and people step forward
> to define them and get a champion on board).
>
> But circling back, I could be well wide of the mark above. If you can give
> us more specifics about use cases that aren't supported, we can probably do
> better helping with them.
>
> [1]: http://www.ecma-international.org/ecma-262/7.0/index.
> html#sec-reflect.construct
>
> [2]: https://github.com/caitp/TC39-Proposals/blob/master/
> tc39-reflect-isconstructor-iscallable.md
>
> -- T.J.
>
> On Thu, Jan 5, 2017 at 5:31 PM, James Treworgy <jamietre at gmail.com> wrote:
>
>> Hi - I am brand new to this list, I find myself here because of a
>> confounding issue related to ES6 classes vs. traditional constructors.
>> Forgive me if this is something that's been hashed out in times past. I
>> looked around for discussion online and couldn't find anything more than
>> the observation that the spec prohibits invoking it - not really any
>> discussion. Probably a failing of google more than anything else, so if
>> there's some discussion that I should read to catch up please point me
>> there.
>>
>> Here's my issue. The ES6 spec prohibits invoking class constructors
>> without "new". This makes such functions a special case, e.g.
>>
>> class Test() {}
>>
>> // typeof Test === 'function'  // yep
>> // Test.prototype.constructor === Test // yep
>>
>> // Test() => nope ... TypeError: Class constructor Test cannot be invoked
>> without 'new'
>> // Test.call() ... nope
>> // Test.apply() ... nope
>>
>> This has some interesting consequences. It means testing something for
>> typeof "function" no longer guarantees it can be invoked without error.
>> Also "function.toString()" can now return something that isn't actually a
>> legal function definiton (since it returns the whole class as text). There
>> seems to be no method, through various javascript reflection/invocation
>> techniques or otherwise, to invoke a class constructor except by creating
>> an instance of the class.
>>
>> For tool-builders the consequences of this are significant. It's no
>> longer possible to create something that can extend/wrap/act on a prototype
>> by intercepting it's construction process, as it was before with plain ES5
>> constructors. So classes are fundamentally different than prototype
>> contructors in how we can use them, far more than syntactic sugar. This has
>> come into play lately for me, as an DI container we use that does exactly
>> this doesn't work with ES6 classes (and as far as I can tell, there's no
>> way to make it work, other than having devs no longer use class syntax).
>>
>> This seems a strange design decision. Even conventional OO languages like
>> C# have the capability to reflect on classes and access the constructor
>> directly as a function. It seems to fly in the face of the basic
>> openness/dyanamic nature of JavaScript, and more signficantly, creates a
>> kind of backward incompatibility since a function is no longer just a
>> function.
>>
>> I'm wondering whether I'm missing some mechanism for legally accessing a
>> class constructor as a function (other than parsing the output of
>> toString() and eval!) -- and generally thoughts on this aspect of the ES6
>> specification.
>>
>> Thank you!
>>
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170105/41bc0f47/attachment-0001.html>


More information about the es-discuss mailing list