> The enforcement seems pointless to me So we're all on the same page, this restriction exists because it means `class` syntax can extend builtin types like `Map`, `Set`, `Array` and `Error` and such, which cannot be extended (with correct functionality anyway) in standard ES5. By delaying initialization of `this`, it allows the constructor to drill all the way to the base class, which gets to instantiate the correct type of object. To have `this` be initialized up front, there would need to be some other mechanism in place to instantiate the correct type of base object.
I feel your pain though, I ran into similar issues when updating our codebase to ES6 classes, though I was able to work around it for our specific usecase by having a base class handle hooking into things. On Thu, Jan 5, 2017 at 1:30 PM, Don Griffin <[email protected]> wrote: > Hi James, > > I too was surprised and disappointed with this restriction on constructors. > > The enforcement seems pointless to me, because there are valid patterns > such as DI or multiple-inheritance where the JS run-time cannot know how a > framework is trying to accomplish the construction sequence. > > This is really counter to the general philosophy of JS (imho) and more > akin to static languages like Java or C#... over there meta programming > (MP) happens in very different ways, but JS had previously made this task > simple... sadly this kind of thing starts to make MP harder at every step. > > I don't hold much hope that this will be relaxed but it should be. :( > > Best, > Don > -- > Don Griffin > Director of Engineering > Sencha, Inc. > https://www.sencha.com/ > > On Thu, Jan 5, 2017 at 1:21 PM, James Treworgy <[email protected]> wrote: > >> > 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 < >> [email protected]> wrote: >> >>> On Thu, Jan 5, 2017 at 5:31 PM, James Treworgy <[email protected]> >>> 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.ht >>> ml#sec-reflect.construct >>> >>> [2]: https://github.com/caitp/TC39-Proposals/blob/master/tc3 >>> 9-reflect-isconstructor-iscallable.md >>> >>> -- T.J. >>> >>> On Thu, Jan 5, 2017 at 5:31 PM, James Treworgy <[email protected]> >>> 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 >>>> [email protected] >>>> https://mail.mozilla.org/listinfo/es-discuss >>>> >>>> >>> >> >> _______________________________________________ >> es-discuss mailing list >> [email protected] >> https://mail.mozilla.org/listinfo/es-discuss >> >> > > _______________________________________________ > es-discuss mailing list > [email protected] > https://mail.mozilla.org/listinfo/es-discuss > >
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

