constructor, super, and data members issue

Jordan Harband ljharb at gmail.com
Tue Sep 4 05:38:40 UTC 2018


I'd say that defining a class directly in a class field is extremely niche,
and by doing that, the user "should know what they've done" too.

On Mon, Sep 3, 2018 at 3:44 PM, Ranando King <kingmph at gmail.com> wrote:

> That scenario is intentional. I see no need to ban it. I would only want
> to ban the confusing case of direct assignment in the outer class
> declaration. For cases where the user intentionally defines a class as you
> have done, they should know that what they've done will create a class that
> is persistently re-defined with each instance. As has been said many times
> before, it's good to reduce the number of foot-guns, but at some point, you
> have to expect some level of responsibility from the programmer. Consider
> that rule as little more than a safety switch.
>
> On Mon, Sep 3, 2018 at 3:52 PM Jordan Harband <ljharb at gmail.com> wrote:
>
>> `field = (function () { return class { }; }())` - how exactly would you
>> propose banning creating a class inside class fields?
>>
>> On Mon, Sep 3, 2018 at 12:05 PM, Ranando King <kingmph at gmail.com> wrote:
>>
>>> I've been thinking about the problems around this some more. At first I
>>> couldn't get past the dissenting arguments from issue #123, but I've since
>>> come up with a solution that might work. What if:
>>>
>>> * Make it illegal to define a class directly on a class field in a class
>>> declaration.
>>> * Move the assignment portion of a class field declaration into a getter
>>> on the prototype such that the getter sets an own property on the instance
>>> if it doesn't exist, then returns that value
>>>
>>> What I mean is this:
>>>
>>> ```js
>>> class Example {
>>>   //classField = class{}; //Error
>>>   otherField=[ "foo", "bar"];
>>> }
>>>
>>> class ES6Example {
>>>   //classField ignored for this example since it was an error.
>>>   get otherField() {
>>>     if ((this instanceof ES6Example) && !this.hasOwnProperty("
>>> otherField"))
>>>       this.otherField = [ "foo", "bar" ];
>>>     return this.otherField;
>>>   }
>>> }
>>> ```
>>>
>>> Done this way, any code expecting early assignment of a field being used
>>> as though it were "abstract" will still work as expected.
>>>
>>> On Thu, Aug 30, 2018 at 4:38 PM doodad-js Admin <doodadjs at gmail.com>
>>> wrote:
>>>
>>>> I'm late to the party, but I've found a solution for my non-loved
>>>> framework : have another constructor called before "super", which fills a
>>>> faked "this" and a faked "args" then replicated values to "this" after
>>>> doing "super(...fakedArgs)".
>>>>
>>>> https://github.com/doodadjs/doodad-js/blob/v9.1.3/src/
>>>> common/Bootstrap.js#L5320-L5330
>>>>
>>>> -----Original Message-----
>>>> From: Isiah Meadows <isiahmeadows at gmail.com>
>>>> Sent: Sunday, August 26, 2018 3:29 PM
>>>> To: Logan Smyth <loganfsmyth at gmail.com>
>>>> Cc: Ben Wiley <therealbenwiley at gmail.com>; es-discuss <
>>>> es-discuss at mozilla.org>
>>>> Subject: Re: constructor, super, and data members issue
>>>>
>>>> Yeah, I was more focused on the static class side of things, because I
>>>> thought they were referring to that. Class instance fields are different,
>>>> and so of course, those are never set on the prototype unless for whatever
>>>> reason, the parent constructor returns `Object.getPrototypeOf(this)`
>>>> instead of letting it default to the normal `this`.
>>>>
>>>> My bad, and you are correct.
>>>>
>>>> -----
>>>>
>>>> Isiah Meadows
>>>> contact at isiahmeadows.com
>>>> www.isiahmeadows.com
>>>>
>>>> On Sun, Aug 26, 2018 at 12:20 PM Logan Smyth <loganfsmyth at gmail.com>
>>>> wrote:
>>>> >
>>>> > Static class fields run their initializers and define the properties
>>>> > at declaration time, and class constructors have the parent class as
>>>> > the `[[Prototype]]`, so static field values are inherited. I think
>>>> this is adding to confusion though, because while that's absolutely true,
>>>> that is not applicable in the same way to non-static class fields, which is
>>>> what this original email is focused on. You could indeed also address this
>>>> with static properties in a proper ES6 environment as ``` class Base {
>>>> >   static idAttribute = "id";
>>>> >
>>>> >   constructor() {
>>>> >        this.idAttribute = new.target.idAttribute;
>>>> >   }
>>>> > }
>>>> > class Derived extends Base {
>>>> >   static idAttribute = "_id";
>>>> >
>>>> >   constructor() {
>>>> >        super();
>>>> >   }
>>>> > }
>>>> > ```
>>>> >
>>>> > On Sun, Aug 26, 2018 at 10:35 AM Isiah Meadows <
>>>> isiahmeadows at gmail.com> wrote:
>>>> >>
>>>> >> Every object, including functions, have an internal prototype.
>>>> Functions normally have one set to `Function.prototype`, and objects
>>>> normally inherit from `Object.prototype` at least indirectly. But because
>>>> of how prototypes work, the only requirement for something to be used as a
>>>> prototype is that it must be an object. So you can do
>>>> `Object.create(someFunction)` and although you can't call it (it's not a
>>>> callable object), that object inherits all the properties and methods from
>>>> that function. `class` in JavaScript is just sugar over a common pattern
>>>> (really complex sugar requiring `new.target` to emulate, but still sugar),
>>>> not an entirely new concept, and it all builds off of prototypes.
>>>> Specifically, the instance prototype inherits from the parent prototype,
>>>> and the class constructor itself inherits from the parent constructor.
>>>> That's why if you declare a static `call` method on a parent class, you can
>>>> still access and use it in the subclass.
>>>> >> On Sat, Aug 25, 2018 at 19:58 Ben Wiley <therealbenwiley at gmail.com>
>>>> wrote:
>>>> >>>
>>>> >>> How can they be prototypically inherited if they don't live on the
>>>> prototype? I feel like I'm missing something.
>>>> >>>
>>>> >>> Le sam. 25 août 2018 19 h 53, Isiah Meadows <isiahmeadows at gmail.com>
>>>> a écrit :
>>>> >>>>
>>>> >>>> Class fields are prototypically inherited just like via `Object
>>>> create`. This is more useful than you might think, and it's the main reason
>>>> anyone actually cares about static fields beyond namespacing.
>>>> >>>> On Sat, Aug 25, 2018 at 14:36 Ben Wiley <therealbenwiley at gmail.com>
>>>> wrote:
>>>> >>>>>
>>>> >>>>> All this just reminds me of *my opinion* that class fields is a
>>>> borrowed concept from statically typed languages that is misplaced in a
>>>> dynamically typed languages like JavaScript.
>>>> >>>>>
>>>> >>>>> In C++ I use class fields to declare what properties will be
>>>> allocated and instantiated when a new class member is constructed.
>>>> >>>>>
>>>> >>>>> In the ES proposal for class fields we mimic this type of
>>>> behavior by instantiating properties on the object when it's constructed,
>>>> but there's no runtime guarantee that this set of properties will remain
>>>> the same.
>>>> >>>>>
>>>> >>>>> There's no reason not to put this in the constructor, and
>>>> although putting class fields on the prototype is debatably not the best
>>>> idea, it would be the only scenario where we get some kind of new helpful
>>>> behavior out of it.
>>>> >>>>>
>>>> >>>>> Ben
>>>> >>>>>
>>>> >>>>> Le sam. 25 août 2018 14 h 25, Augusto Moura <
>>>> augusto.borgesm at gmail.com> a écrit :
>>>> >>>>>>
>>>> >>>>>> 24-08-2018 19:29, Aaron Gray <aaronngray.lists at gmail.com>:
>>>> >>>>>>
>>>> >>>>>> >
>>>> >>>>>> > Yeah it does look like its badly "broken by design".
>>>> >>>>>> >
>>>> >>>>>>
>>>> >>>>>> Why this behaviour is broken? Every OOP language that I worked
>>>> >>>>>> with behaves de same way, and there's not many developers
>>>> >>>>>> complaining about it. If you want to use a property that might
>>>> be
>>>> >>>>>> overrided in a subclasss you need to use a method and make the
>>>> >>>>>> source of the data more versatile (in Java and others similiar
>>>> >>>>>> languages we have to implement it using getter methods). Luckily
>>>> >>>>>> Javascript doesn't need getter and setters methods to make a
>>>> >>>>>> property overridable because of getter and setters descriptors,
>>>> >>>>>> so we can workaround the first example
>>>> >>>>>> easily:
>>>> >>>>>>
>>>> >>>>>> ``` js
>>>> >>>>>> class Bar {
>>>> >>>>>>   bar = 'in bar';
>>>> >>>>>>
>>>> >>>>>>   constructor() {
>>>> >>>>>>     console.log(this.bar)
>>>> >>>>>>   }
>>>> >>>>>> }
>>>> >>>>>>
>>>> >>>>>> class Foo extends Bar {
>>>> >>>>>>   _initiedSuper = false;
>>>> >>>>>>   _bar = 'in foo';
>>>> >>>>>>
>>>> >>>>>>   constructor() {
>>>> >>>>>>     super();
>>>> >>>>>>     this._initiedSuper = true;
>>>> >>>>>>   }
>>>> >>>>>>
>>>> >>>>>>   get bar() {
>>>> >>>>>>     return this._bar;
>>>> >>>>>>   }
>>>> >>>>>>
>>>> >>>>>>   set bar(val) {
>>>> >>>>>>     if (this._initiedSuper) {
>>>> >>>>>>       this._bar = val;
>>>> >>>>>>     }
>>>> >>>>>>   }
>>>> >>>>>> }
>>>> >>>>>>
>>>> >>>>>> new Foo(); // will log 'in foo'
>>>> >>>>>> ```
>>>> >>>>>>
>>>> >>>>>> *I have to say the relaying that the super constructor will use
>>>> >>>>>> the bar property and workarounding it **is a bad practice** and
>>>> >>>>>> should be avoided at any costs. The contract with the super
>>>> class
>>>> >>>>>> constructor should rely only on the super call, these situations
>>>> >>>>>> just reveal bad design choices in the super class. Logan Smyth
>>>> >>>>>> example is the correct answer to this problem*
>>>> >>>>>>
>>>> >>>>>>
>>>> >>>>>> 25-08-2018 01:28, Jordan Harband <ljharb at gmail.com>:
>>>> >>>>>>
>>>> >>>>>> >
>>>> >>>>>> > Personally I think a design where the superclass relies on any
>>>> >>>>>> > part of the subclass is "broken by design"; but certainly
>>>> >>>>>> > there's ways you can achieve that.
>>>> >>>>>> >
>>>> >>>>>>
>>>> >>>>>> Of course is not broken. The super class has a contract with a
>>>> >>>>>> parametrized option, it can be used in subclasses or just in a
>>>> >>>>>> constructor call `new Base({ idAttribute: 'foo' })`, if it has a
>>>> >>>>>> default value for that is not a sub class concern. When
>>>> >>>>>> refactoring code adding defaults and "lifting" parameters are
>>>> >>>>>> very common ~not only on OOP~ and relying that the super class
>>>> is
>>>> >>>>>> using some property in the constructor is the real "broken by
>>>> design".
>>>> >>>>>> _______________________________________________
>>>> >>>>>> es-discuss mailing list
>>>> >>>>>> es-discuss at mozilla.org
>>>> >>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>> >>>>>
>>>> >>>>> _______________________________________________
>>>> >>>>> es-discuss mailing list
>>>> >>>>> es-discuss at mozilla.org
>>>> >>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>> >>
>>>> >> _______________________________________________
>>>> >> es-discuss mailing list
>>>> >> es-discuss at mozilla.org
>>>> >> https://mail.mozilla.org/listinfo/es-discuss
>>>>
>>>>
>>>>
>>>> ---
>>>> This email has been checked for viruses by AVG.
>>>> https://www.avg.com
>>>>
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>
>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>>
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180903/d21d5955/attachment-0001.html>


More information about the es-discuss mailing list