Why are object initializer methods not usable as constructors?

/#!/JoePea joe at trusktr.io
Tue May 16 00:25:32 UTC 2017


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


More information about the es-discuss mailing list