T.J. Crowder (2017-01-06T13:34:19.000Z)
tj.crowder at farsightsoftware.com (2017-01-06T13:36:53.562Z)
Note: Related discussion of calling class constructors here: https://esdiscuss.org/topic/determine-if-a-value-is-callable-constructible On Fri, Jan 6, 2017 at 12:11 PM, James Treworgy <jamietre at gmail.com> wrote: > T.J. Thanks for the very thoughtful analysis. But I keep coming back to > this: > >> since we >> wouldn't want `call`/`apply` to allow violating the "no `this` before >> `super(...)`" rule by setting the `this` binding early. > > Why? > > To me the best solution is also the simplest - just let people do this. On first blush, a couple of reasons, but that doesn't mean they aren't addressable: 1. I think the supposed simplicity is illusory. If you get into the mechanics of actually "just" allowing people to do it with `call`/`apply`, as I said earlier, I think it looks very like `Reflect.new` but with less flexbility and with the "`this` before `super()`" problem. Do you have a proposal for how to actually do it that doesn't have similar complexity? You'd have to modify `call` and `apply`, you'd still have to modify how Construct and [[Construct]] work (at the very least). Maybe you get some savings by allowing a `this` binding before `super` (you might avoid the hook slot on the environment), but my instinct is the savings are small change. But I haven't gone through the same level of analysis on it as I did with `Reflect.new`, so I'm open to being proved wrong on that. Granted *using* them would be simpler **if** you didn't care whether you're calling a constructor vs. non-constructor (e.g., you "just" use `call`/`apply`). But is it really the case that there's a lot of code that doesn't need to care? Calls and construction are very different things. But a win for overloading `call`/`apply` is, of course, that ES5 constructor code can subclass an ES2015+ class constructor without branching. Existing ES5 code could be fed an ES2015+ class constructor and (probably) Just Work™. But I don't know that it's a common use case outside DI frameworks, which are relatively few and thus upgradeable. 2. Having `call`/`apply` not work the same way a function call does (modulo their actual purpose) is a significant departure from how they've worked since they were added to JavaScript originally. Now every explanation of them must not only say "They call the function setting a specific value for `this`" but also "**and** they let you bypass the restriction preventing calling constructors as functions." Okay, so the counter-argument could be that allowing them to call a constructor as a function is a by-product of their main purpose (setting `this`), but to me it's both different and surprising. 3. Allowing `this` prior to `super()` in a subclass constructor depending on how it's called makes it impossible for lint tools and such to flag that up early as an error. So they can't support the majority case where that's going to be an error at runtime. Perhaps all of those can be addressed or dismissed. I don't want to give the impression of being married to anything (not that my opinion is particularly important anyway). But my gut says overloading `call`/`apply` (thus allowing early `this` binding) isn't markedly simpler, but is more problematic. Side note: In my outline of `Reflect.new`, I should have flagged up that we'd really want at least the `Reflect.isConstructor` part of [this proposal](https://github.com/caitp/TC39-Proposals/blob/master/tc39-reflect-isconstructor-iscallable.md) to be in place. DI systems would need to know what they were dealing with. But (to me, anyway) the `Reflect.isConstructor` part of that proposal is non-controversial (`Reflect.isCallable` has an open question about what it should say for class constructors -- how relevant!). I feel like I've done a lot of talking on this; I'm going to hang back and just listen for a while. :-) -- T.J.