Accesssing ES6 class constructor function

James Treworgy jamietre at gmail.com
Fri Jan 6 14:03:18 UTC 2017


> 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

In ES5 the only thing that makes a constructor behave differently is
invoking it with `new`. There's no difference between a constructor and a
regular function, there's just a different way to invoke it. So yeah - I
don't care.

> 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 original

I see your point but I don't exactly agree. It's not that they work
differently, it's just that you aren't allowed to invoke once specific type
of function directly. (And again if I had my way, I'd just drop the 'no
new' restriction entirely for the sake of 100% backward compatibility
between class vs. prototype syntax -- but I don't personally care about the
`new` thing).

I'd make the counterpoint that the way things are now, we have a much more
dramatic departure from the way things worked since the beginning: I can
create a function that I have no ability to access directly through any
means. There has never been a "black box" process in Javascript before, now
there is. I could completely simulate the prototype chain construction
process and rewire it any way I wanted, now I can't. This is a big
functional and philosophical change. But mostly - from a practical
standpoint - it means that "class" syntax is not backwards compatible with
prototype syntax. I think that most developers would expect that with the
exception of builtins that never worked correctly using the old
instantiation patterns, everything else should work interchangeably. You
should be able to write simple classes using simple inhertance patterns and
have `class` just act as syntactic sugar, unless you're trying to do
something that wasn't possible before.

I get that a class is not supposed to be exactly the same as a prototype
constructor, since one of the goals was to fix the problem extending
builtins, but it _should_ be backwards compatible. Right now it's not in a
very significant way.

> The semantics of `super()` would need to be addressed, in the sense that
it too would have to be permitted to use an existing context in this
circumstance.

This is true. Not being familiar with the implementation details I can't
say how big a deal this is, but yes - the ability to pass a context down
through `super` (the same as you did old-school by invoking the superclass
constructor directly against your own context from within the subclass
constructor) is required. To me, this is just backwards compatibility.

Thanks again for your time and engagement here. This is a pretty important
issue to me, and I'm definitely not knowledgable about the implemenetation
details. I am very gratified to be able to get into it here and learn more.



On Fri, Jan 6, 2017 at 8:34 AM, T.J. Crowder <
tj.crowder at farsightsoftware.com> wrote:

> 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.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170106/778cbe3f/attachment-0001.html>


More information about the es-discuss mailing list