Why are object initializer methods not usable as constructors?

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

Why are object initializer methods not usable as constructors?

/#!/JoePea
I feel like recent changes in the language (ES6+) introduce new features that don't have the flexibility as the pre ES6 language that we're used to.

For example, [`super` is static and inflexible](https://esdiscuss.org/topic/the-super-keyword-doesnt-work-as-it-should) which is not inline with how `this` works.

Object initializer methods are also limited. Suppose I want to define an object that contains various constructors. I would be inclined to use object-initializer shortcuts:

```js
let classes = {
  Cat() {},
  Dog() {},
  Bird() {}
}
```

but this doesn't work:

```js
new classes.Dog() // Uncaught TypeError: classes.Dog is not a constructor
```

So, here's another failure of intuition (`super` being static was also not intuitive). We can fix this by writing:

```js
function Cat() {}
function Dog() {}
function Bird() {}

let classes = {
  Cat,
  Dog,
  Bird
}
```

but that's not as convenient. What's the reason why we shouldn't be able to do that? I feel like JavaScript is being restricted in undesirable ways. I love JavaScript because it has always been so flexible, and I would expect the new features to continue being flexible. That's what makes JavaScript great.

/#!/JoePea

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

Andrea Giammarchi-2
IIRC methods shorthand are the only where `super` is allowed, at least in Chrome and Firefox.

Accordingly, if you use them as classes, the super won't point at the one you'd expect, it would point to the object hosting those classes as properties.

```js
class Animal { setName(name) { this.name = name; } }
class Cat extends Animal { constructor() { super(); super.setName('cat'); }}
let classes = {
  __proto__: {setName: 'nope'}
  Cat
};

new classes.Cat(); // ?
```

I guess you cannot have the best from both worlds.



On Wed, Jul 27, 2016 at 3:49 AM, /#!/JoePea <[hidden email]> wrote:
I feel like recent changes in the language (ES6+) introduce new features that don't have the flexibility as the pre ES6 language that we're used to.

For example, [`super` is static and inflexible](https://esdiscuss.org/topic/the-super-keyword-doesnt-work-as-it-should) which is not inline with how `this` works.

Object initializer methods are also limited. Suppose I want to define an object that contains various constructors. I would be inclined to use object-initializer shortcuts:

```js
let classes = {
  Cat() {},
  Dog() {},
  Bird() {}
}
```

but this doesn't work:

```js
new classes.Dog() // Uncaught TypeError: classes.Dog is not a constructor
```

So, here's another failure of intuition (`super` being static was also not intuitive). We can fix this by writing:

```js
function Cat() {}
function Dog() {}
function Bird() {}

let classes = {
  Cat,
  Dog,
  Bird
}
```

but that's not as convenient. What's the reason why we shouldn't be able to do that? I feel like JavaScript is being restricted in undesirable ways. I love JavaScript because it has always been so flexible, and I would expect the new features to continue being flexible. That's what makes JavaScript great.

/#!/JoePea

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

Allen Wirfs-Brock
The most common case is the “methods” are designed to be used as constructors.  This has always be recognized for built-in methods in the ECMAScript specification  which says: "Built-in function objects that are not identified [in this specification] as constructors do not implement the [[Construct]] internal method unless otherwise specified in the description of a particular function.”.   When “concise methods” where add as part of Object Initializers and Class Definitinos this default was applied to them.  

You can still define constructible properties for Object Initializers, just not by using concise method syntax:

```js
let classes = {
  Cat: function() {},
  Dog: function() {},
  Bird: class {}
};

let [c, d, b] = [new classes.Cat, new classes.Dog, new classes.Bird];
```





_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

/#!/JoePea
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?

The problem here would be solved with a dynamic super.

I feel that ES6 diverges from ES5 in backwards-incompatible ways. Some people say ES6 classes are "syntax sugar" over the ES5 constructor pattern classes, but, *not really*, since they aren't compatible in the same places. ES6 classes  and object-initializer methods are not backwards-incompatible in a bad way because they cannot be treated the same as we are used to in ES5 (f.e. copying methods with underscore's `_.extend()` doesn't work because of static HomeObjects), and that is backwards-incompatible with previous concepts, ideas, and methodologies that we love JavaScript for.

Some old code might do something like

```js
_.extend(SomeClass.prototype, OtherClass.prototype)
```

where `SomeClass` and `OtherClass` are ES5 constructor-pattern classes. If the classes are updated to be ES6 classes, then that code may fail because of the static `HomeObject` properties, and that is backwards-incompatible with the paradigms and patterns of ES5.

**A dynamic super would remedy this problem.**

For now, I am contemplating using a custom and dynamic `Object.prototype.super` getter in all apps where my code runs so that my ES6 classes remain as flexible as in ES5. The overhead associated with a custom `Object.prototype.super` would only be due to the fact that it is not possible for us to hook into the native property lookup algorithm, otherwise the overhead of finding a HomeObject would be completely negligible because the native property lookup algorithm already finds the HomeObject where a property or method is located and therefore it would cost next to nothing to use the found HomeObject on a method call by passing HomeObject as an argument in the native side of the JS engine.


/#!/JoePea

On Wed, Jul 27, 2016 at 7:58 AM, Allen Wirfs-Brock <[hidden email]> wrote:
The most common case is the “methods” are designed to be used as constructors.  This has always be recognized for built-in methods in the ECMAScript specification  which says: "Built-in function objects that are not identified [in this specification] as constructors do not implement the [[Construct]] internal method unless otherwise specified in the description of a particular function.”.   When “concise methods” where add as part of Object Initializers and Class Definitinos this default was applied to them.

You can still define constructible properties for Object Initializers, just not by using concise method syntax:

```js
let classes = {
  Cat: function() {},
  Dog: function() {},
  Bird: class {}
};

let [c, d, b] = [new classes.Cat, new classes.Dog, new classes.Bird];
```







_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

/#!/JoePea
> IIRC methods shorthand are the only where super is allowed, at least in Chrome and Firefox.

If `super` were dynamic, then `super` could be allowed anywhere, not just in Classes or object-initializer shorthand methods.

For what it's worth, `Object.assign` is a concept promoted into the language by pre-ES6 ideas, like underscore's `_.extend` and others. So, the  mere fact that `Object.assign` will fail to produce expected results when copying something from an ES6 prototype that uses `super` means that the direction of the ES6+ language is backwards-incompatible with pre-ES6 JavaScript and that ES6+ features are incompatible with themselves.

If something like `Function.prototype.toMethod` made it into spec but not a dynamic `super`, then solution would be half-complete: tools like `Object.assign` would also need to start using `toMethod` internally so that results would be intuitive.

Arguably, the best solution would be for `super` to be dynamic and for `Function.prototype.toMethod` to exist, following the same patterns as we *love* about JavaScript's `this` keyword. This would open up meta-programming possibilities, for example it would then be easy to write a [prototype-based implementation of multiple inheritance](https://gist.github.com/trusktr/8c515f7bd7436e09a4baa7a63cd7cc37) whereby a single prototype chain is created based on the multiple classes we wish to extend from (this is in constrast to a [`Proxy`-based implementation](https://gist.github.com/trusktr/05b9c763ac70d7086fe3a08c2c4fb4bf)).

Claude Pache [mentioned that he likes for method borrowing to maintain the same HomeObject](https://esdiscuss.org/topic/the-super-keyword-doesnt-work-as-it-should#content-16), but in my opinion that does not stay inline intuitive expectations coming from pre-ES6 with a dynamic `this`: "I intuitively expect super to be relative to the prototype chain of the current object where a method is called".

If you are technical enough to understand why method borrowing fails due to a static `super`, then you'd also be technical enough to understand why it works the dynamic way if that ever became reality. Plus, if `Function.prototype.toMethod` is brought into spec, then you'd also be technical enough to borrow the method and assign the original HomeObject that you wish.

/#!/JoePea

On Wed, Jul 27, 2016 at 11:44 AM, /#!/JoePea <[hidden email]> 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?

The problem here would be solved with a dynamic super.

I feel that ES6 diverges from ES5 in backwards-incompatible ways. Some people say ES6 classes are "syntax sugar" over the ES5 constructor pattern classes, but, *not really*, since they aren't compatible in the same places. ES6 classes  and object-initializer methods are not backwards-incompatible in a bad way because they cannot be treated the same as we are used to in ES5 (f.e. copying methods with underscore's `_.extend()` doesn't work because of static HomeObjects), and that is backwards-incompatible with previous concepts, ideas, and methodologies that we love JavaScript for.

Some old code might do something like

```js
_.extend(SomeClass.prototype, OtherClass.prototype)
```

where `SomeClass` and `OtherClass` are ES5 constructor-pattern classes. If the classes are updated to be ES6 classes, then that code may fail because of the static `HomeObject` properties, and that is backwards-incompatible with the paradigms and patterns of ES5.

**A dynamic super would remedy this problem.**

For now, I am contemplating using a custom and dynamic `Object.prototype.super` getter in all apps where my code runs so that my ES6 classes remain as flexible as in ES5. The overhead associated with a custom `Object.prototype.super` would only be due to the fact that it is not possible for us to hook into the native property lookup algorithm, otherwise the overhead of finding a HomeObject would be completely negligible because the native property lookup algorithm already finds the HomeObject where a property or method is located and therefore it would cost next to nothing to use the found HomeObject on a method call by passing HomeObject as an argument in the native side of the JS engine.


/#!/JoePea

On Wed, Jul 27, 2016 at 7:58 AM, Allen Wirfs-Brock <[hidden email]> wrote:
The most common case is the “methods” are designed to be used as constructors.  This has always be recognized for built-in methods in the ECMAScript specification  which says: "Built-in function objects that are not identified [in this specification] as constructors do not implement the [[Construct]] internal method unless otherwise specified in the description of a particular function.”.   When “concise methods” where add as part of Object Initializers and Class Definitinos this default was applied to them.

You can still define constructible properties for Object Initializers, just not by using concise method syntax:

```js
let classes = {
  Cat: function() {},
  Dog: function() {},
  Bird: class {}
};

let [c, d, b] = [new classes.Cat, new classes.Dog, new classes.Bird];
```








_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

Andrea Giammarchi-2
I hope you're not using `Object.assign` to copy anything different from basic setup-like or arguments objects: if I were you I would use `Object.defineProperties(targetProto, Object.getOwnPropertyDescriptors(superProto))` or it gonna be "bad time".

I think they answered already on the dynamic super, not sure keep raising the issue would help :-(

Best Regards

On Wed, Jul 27, 2016 at 8:13 PM, /#!/JoePea <[hidden email]> wrote:
> IIRC methods shorthand are the only where super is allowed, at least in Chrome and Firefox.

If `super` were dynamic, then `super` could be allowed anywhere, not just in Classes or object-initializer shorthand methods.

For what it's worth, `Object.assign` is a concept promoted into the language by pre-ES6 ideas, like underscore's `_.extend` and others. So, the  mere fact that `Object.assign` will fail to produce expected results when copying something from an ES6 prototype that uses `super` means that the direction of the ES6+ language is backwards-incompatible with pre-ES6 JavaScript and that ES6+ features are incompatible with themselves.

If something like `Function.prototype.toMethod` made it into spec but not a dynamic `super`, then solution would be half-complete: tools like `Object.assign` would also need to start using `toMethod` internally so that results would be intuitive.

Arguably, the best solution would be for `super` to be dynamic and for `Function.prototype.toMethod` to exist, following the same patterns as we *love* about JavaScript's `this` keyword. This would open up meta-programming possibilities, for example it would then be easy to write a [prototype-based implementation of multiple inheritance](https://gist.github.com/trusktr/8c515f7bd7436e09a4baa7a63cd7cc37) whereby a single prototype chain is created based on the multiple classes we wish to extend from (this is in constrast to a [`Proxy`-based implementation](https://gist.github.com/trusktr/05b9c763ac70d7086fe3a08c2c4fb4bf)).

Claude Pache [mentioned that he likes for method borrowing to maintain the same HomeObject](https://esdiscuss.org/topic/the-super-keyword-doesnt-work-as-it-should#content-16), but in my opinion that does not stay inline intuitive expectations coming from pre-ES6 with a dynamic `this`: "I intuitively expect super to be relative to the prototype chain of the current object where a method is called".

If you are technical enough to understand why method borrowing fails due to a static `super`, then you'd also be technical enough to understand why it works the dynamic way if that ever became reality. Plus, if `Function.prototype.toMethod` is brought into spec, then you'd also be technical enough to borrow the method and assign the original HomeObject that you wish.

/#!/JoePea

On Wed, Jul 27, 2016 at 11:44 AM, /#!/JoePea <[hidden email]> 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?

The problem here would be solved with a dynamic super.

I feel that ES6 diverges from ES5 in backwards-incompatible ways. Some people say ES6 classes are "syntax sugar" over the ES5 constructor pattern classes, but, *not really*, since they aren't compatible in the same places. ES6 classes  and object-initializer methods are not backwards-incompatible in a bad way because they cannot be treated the same as we are used to in ES5 (f.e. copying methods with underscore's `_.extend()` doesn't work because of static HomeObjects), and that is backwards-incompatible with previous concepts, ideas, and methodologies that we love JavaScript for.

Some old code might do something like

```js
_.extend(SomeClass.prototype, OtherClass.prototype)
```

where `SomeClass` and `OtherClass` are ES5 constructor-pattern classes. If the classes are updated to be ES6 classes, then that code may fail because of the static `HomeObject` properties, and that is backwards-incompatible with the paradigms and patterns of ES5.

**A dynamic super would remedy this problem.**

For now, I am contemplating using a custom and dynamic `Object.prototype.super` getter in all apps where my code runs so that my ES6 classes remain as flexible as in ES5. The overhead associated with a custom `Object.prototype.super` would only be due to the fact that it is not possible for us to hook into the native property lookup algorithm, otherwise the overhead of finding a HomeObject would be completely negligible because the native property lookup algorithm already finds the HomeObject where a property or method is located and therefore it would cost next to nothing to use the found HomeObject on a method call by passing HomeObject as an argument in the native side of the JS engine.


/#!/JoePea

On Wed, Jul 27, 2016 at 7:58 AM, Allen Wirfs-Brock <[hidden email]> wrote:
The most common case is the “methods” are designed to be used as constructors.  This has always be recognized for built-in methods in the ECMAScript specification  which says: "Built-in function objects that are not identified [in this specification] as constructors do not implement the [[Construct]] internal method unless otherwise specified in the description of a particular function.”.   When “concise methods” where add as part of Object Initializers and Class Definitinos this default was applied to them.

You can still define constructible properties for Object Initializers, just not by using concise method syntax:

```js
let classes = {
  Cat: function() {},
  Dog: function() {},
  Bird: class {}
};

let [c, d, b] = [new classes.Cat, new classes.Dog, new classes.Bird];
```









_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

Tab Atkins Jr.
In reply to this post by /#!/JoePea
On Wed, Jul 27, 2016 at 11:44 AM, /#!/JoePea <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

/#!/JoePea
>  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. <[hidden email]> wrote:
On Wed, Jul 27, 2016 at 11:44 AM, /#!/JoePea <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

/#!/JoePea
> 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 <[hidden email]> 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. <[hidden email]>
> wrote:
>>
>> On Wed, Jul 27, 2016 at 11:44 AM, /#!/JoePea <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

Jordan Harband
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 use `class`? That's what users who have access to ES6+ environments are likely to do anyways.

It's also worth noting that someone could do `constructor: () => {}` or `constructor: function *() {}` and `new`ing them would fail the same way.

On Mon, May 15, 2017 at 5:25 PM, /#!/JoePea <[hidden email]> 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 <[hidden email]> 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. <[hidden email]>
> wrote:
>>
>> On Wed, Jul 27, 2016 at 11:44 AM, /#!/JoePea <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

T.J. Crowder-2
Methods not being constructors also makes them lighter-weight: They have no `prototype` property and associated object.

This isn't limiting developer creativity or freedom. If you want to create an object with constructor functions, you have at least two ways to do that:

1. `class`:

```js
let ctors = {
    Foo: class { },
    Bar: class { }
};
```

2. `function`:

```js
let ctors = {
    Foo: function { },
    Bar: function { }
};
```

> but most JS authors who don't know about these pesky new JavaScript
> language internals might be inclined to write:

Then the library author warns them not to do that in the documentation (maybe even a really clear error message in the non-min build), or they figure it out really quickly when they do and it doesn't work. :-)

-- T.J. Crowder


On Tue, May 16, 2017 at 2:16 AM, Jordan Harband <[hidden email]> 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 use `class`? That's what users who have access to ES6+ environments are likely to do anyways.

It's also worth noting that someone could do `constructor: () => {}` or `constructor: function *() {}` and `new`ing them would fail the same way.

On Mon, May 15, 2017 at 5:25 PM, /#!/JoePea <[hidden email]> 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 <[hidden email]> 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. <[hidden email]>
> wrote:
>>
>> On Wed, Jul 27, 2016 at 11:44 AM, /#!/JoePea <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

/#!/JoePea
In reply to this post by Jordan Harband
/#!/JoePea


On Mon, May 15, 2017 at 6:16 PM, Jordan Harband <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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. <[hidden email]>
>> > wrote:
>> >>
>> >> On Wed, Jul 27, 2016 at 11:44 AM, /#!/JoePea <[hidden email]> 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
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
>
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Why are object initializer methods not usable as constructors?

Michael J. Ryan
But if you're introducing a new Class library, your writing new code... You could almost as really setup a build chain (Babel) to support class syntax, if you can't already target sorted browsers.


--
Michael J. Ryan - [hidden email] - http://tracker1.info

Please excuse grammar errors and typos, as this message was sent from my phone.

On May 16, 2017 4:51 PM, "/#!/JoePea" <[hidden email]> wrote:
/#!/JoePea


On Mon, May 15, 2017 at 6:16 PM, Jordan Harband <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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. <[hidden email]>
>> > wrote:
>> >>
>> >> On Wed, Jul 27, 2016 at 11:44 AM, /#!/JoePea <[hidden email]> 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
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
>
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss