Why are object initializer methods not usable as constructors?
/#!/JoePea
joe at trusktr.io
Tue May 16 23:50:39 UTC 2017
/#!/JoePea
On Mon, May 15, 2017 at 6:16 PM, Jordan Harband <ljharb at gmail.com> wrote:
> You seem to be suggesting that ES6 should be making it easier for you to
> reimplement a core ES6 language feature. If you want `class`, can you not
Yeah, sure, why not? Allow library authors to do awesome things like
provide libraries for multiple inheritance that aren't hacks.
```js
import Bar from './Bar'
import Baz from './Baz'
import multiple from 'multiple-inheritance-library-by-some-author'
class Foo extends multiple(Bar, Baz) {}
```
> use `class`? That's what users who have access to ES6+ environments are
> likely to do anyways.
ES6 classes don't have protected or private members, but an author's
`Class` tool might.
Not everyone is writing ES6 in a shiny new app. There's large outdated
code bases. It'd be convenient for a tool like `Class` to work on old
code, and not fail on new code.
>
> It's also worth noting that someone could do `constructor: () => {}` or
> `constructor: function *() {}` and `new`ing them would fail the same way.
Yes, we can't prevent all the bad usages, but those are obviously not
meant to work. Concise methods aren't obviously going to fail,
especially considering that they are not really called "concise
methods" by most people, but more like "shorthands", and by that
terminology the layman is going to expect them to work. Arrow function
and generatos are very explicitly different, and you have to actually
know what they are in order to use them.
There's always going to be some way to make some library fail, but
that doesn't mean we should add more ways to make code fail when we
can avoid it. Arrow functions and generators are necessary for a
purpose. However, making concise methods that don't use the keyword
`super` non-constructable doesn't really have any great purpose, it
only makes certain code fail in needless ways...
>
> On Mon, May 15, 2017 at 5:25 PM, /#!/JoePea <joe at trusktr.io> wrote:
>>
>> > Because they're methods, not functions. The distinction between the
>> > two was merely semantic in ES5, but now it's mechanical, due to
>> > super(); constructing something intended as a method would make
>> > super() behave in confusing and unintuitive ways, so methods just
>> > don't have a constructor any more.
>>
>> You are correct for app authors, but wrong for library authors. A
>> library author may easily like to make an object that contains ES5
>> constructors, and writing them like
>>
>> ```
>> let ctors = {
>> Foo() {},
>> Bar() {},
>> }
>> ```
>>
>> is simply simple.
>>
>> For example, suppose a library author releases a `Class` function for
>> defining classes, it could be used like this:
>>
>> ```
>> const Animal = Class({
>> constructor: function() {
>> console.log('new Animal')
>> }
>> })
>> ```
>>
>> but most JS authors who don't know about these pesky new JavaScript
>> language internals might be inclined to write:
>>
>>
>> ```
>> const Animal = Class({
>> constructor() {
>> console.log('new Animal')
>> }
>> })
>> ```
>>
>> If the `Class` implementation returns that "constructor", then when
>> the user does `new Animal` they will get an unexpected error, and that
>> is not ideal at all for a dynamic language like JavaScript.
>>
>> What's even stranger is that the `Class` implementation can wrap the
>> concise method with a [[Construct]]able function and call the concise
>> method with `.call` or `.apply` and it will work! But that is simply
>> just messy and ugly.
>>
>> So why prevent it from working only sometimes? It would be much better
>> for it to just work all the time, and make restrictions only when
>> `super` is present in the function body. In the above example, the
>> `constructor() {}` concise method does not use the keyword `super`, so
>> treating it like `constructor: function constructor() {}` would be
>> much more ideal.
>>
>> We shouldn't limit developer creativity for reasons that don't exist
>> (I haven't heard of any compelling reasons so far).
>>
>> The new language features cause failures in unexpected ways, and I
>> really don't think the language should be designed in this
>> less-intuitive way.
>>
>> JavaScript pre-ES6 has a dynamic nature, but ES6 and newer features
>> are less-so in that tradition.
>>
>> > Making this one niche use-case ("I want to define several
>> > constructor-only classes inline in an object initializer") require a few
>> > characters less is not a sufficiently worthwhile benefit for the cost.
>>
>> Actually, no, I want to allow end-users of my library to pass in
>> objects containing methods and properties, and I don't want the result
>> to fail in unexpected ways, and I also don't want to write ugly and
>> hacky code to make it work.
>>
>> > Just type the few extra characters (exactly what you would have typed in
>> > ES5, so it's not even a new imposition), and you'll be fine.
>>
>> Like I said, it won't be me typing these things, it will be end users.
>> Yes I can disguise the problem, but if I for example were implementing
>> a `Class` tool, I wouldn't like to wrap their non-constructable
>> methods in a proxy function just to make it work not only because it
>> is ugly, but because it will show things in the console that are more
>> difficult to debug.
>>
>> For example, have you ever looked at classes made with Backbone? They
>> are not so nice to inspect because Backbone wraps constructors and
>> prototypes like an onion.
>>
>> This language "feature" of concise methods that makes them not
>> constructable forces library authors to write ugly code who's output
>> is harder to inspect and debug by end developers.
>> /#!/JoePea
>>
>>
>> On Sat, Jul 30, 2016 at 3:04 AM, /#!/JoePea <joe at trusktr.io> wrote:
>> >> The distinction between the two was merely semantic in ES5, but now
>> >> it's
>> >> mechanical, due to super(); constructing something intended as a method
>> >> would make super() behave in confusing and unintuitive ways, so methods
>> >> just
>> >> don't have a constructor any more.
>> >
>> > So, if `super` were dynamic, then it would be no problem.
>> >
>> >> It's been explained to you already in previous threads why super() is
>> >> designed the way it is, and how a dynamic super() would add significant
>> >> cost
>> >> to some situations
>> >
>> > Those "some situations" haven't been listed yet (or I don't know where
>> > they
>> > are listed). Do you know any? As far as I can tell, a dynamic super
>> > would
>> > perform just fine:
>> >
>> > - For constructor calls, `HomeObject` can just the `.prototype` property
>> > of
>> > the function when `new.target` is the same as the function being
>> > constructed. That's simple.
>> > - If `new.target` is not the current constructed function, then the
>> > current
>> > function was found on the prototype chain of
>> > `Object.getPrototypeOf(new.target)` (i.e. tje `.constructor` property
>> > was
>> > found on some `HomeObject` in the prototype chain) and then that
>> > function is
>> > called with the found `HomeObject`. This seems like a simple addition to
>> > the
>> > property lookup algorithm.
>> > - Functions called as methods simply have `HomeObject` passed as the
>> > prototype (HomeObject) where they were found. This seems like a simple
>> > addition to the property lookup algorithm.
>> > - What else?
>> >
>> > Based on those ideas from my limited knowledge on thetopic, a dynamic
>> > `super` doesn't seem to "costly".
>> >
>> > Suppose I write
>> >
>> > ```js
>> > obj.foo()
>> > ```
>> >
>> > Then, in ES5, there is already going to be a property lookup algorithm
>> > to
>> > find `foo` on the prototype chain of `obj`. Therefore, when the
>> > property`foo` is found on a prototype (a HomeObject), it doesn't seem
>> > like
>> > all that much extra cost to simply pass that found object by reference
>> > to
>> > the `foo` method call, since we already found it. That's not very
>> > costly.
>> >
>> > I may be using the word "HomeObject" wrong, but I think you get what I
>> > mean.
>> >
>> >
>> > /#!/JoePea
>> >
>> > On Thu, Jul 28, 2016 at 9:11 AM, Tab Atkins Jr. <jackalmage at gmail.com>
>> > wrote:
>> >>
>> >> On Wed, Jul 27, 2016 at 11:44 AM, /#!/JoePea <joe at trusktr.io> wrote:
>> >> > What's the good reason that object-initializer methods can't be
>> >> > constructors
>> >> > though? I mean, I get that "that's what spec says", but what's the
>> >> > actual
>> >> > good reason?
>> >>
>> >> Because they're methods, not functions. The distinction between the
>> >> two was merely semantic in ES5, but now it's mechanical, due to
>> >> super(); constructing something intended as a method would make
>> >> super() behave in confusing and unintuitive ways, so methods just
>> >> don't have a constructor any more.
>> >>
>> >> There are several very similar ways you can write your example that do
>> >> achieve what you want. As Allen said, you can just use the non-concise
>> >> syntax, explicitly typing "function" (or better, "class") for each of
>> >> the values. This is only a few characters more and achieves exactly
>> >> what you want.
>> >>
>> >> It's been explained to you already in previous threads why super() is
>> >> designed the way it is, and how a dynamic super() would add
>> >> significant cost to some situations. Making this one niche use-case
>> >> ("I want to define several constructor-only classes inline in an
>> >> object initializer") require a few characters less is not a
>> >> sufficiently worthwhile benefit for the cost. Just type the few extra
>> >> characters (exactly what you would have typed in ES5, so it's not even
>> >> a new imposition), and you'll be fine.
>> >>
>> >> ~TJ
>> >
>> >
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>
>
More information about the es-discuss
mailing list