Proposal for a static constructor and static member variables

Isiah Meadows isiahmeadows at gmail.com
Thu Jan 26 18:57:17 UTC 2017


There is this proposal for `static` blocks, which IMHO would fit
better with the intent:
https://github.com/littledan/proposal-class-static-block

Class initialization isn't the same as object initialization. Classes
are only initialized once, and parameters don't make sense, so it
doesn't make sense to make it a method-like construct. Getters are
still function-like, and they're readable via descriptors. So such a
"static constructor" would in practice be closer to a special IIFE
that's run in the context of the class.

In addition, does it make sense to re-run the `static` block with
subclasses? Generally, it doesn't, because subclasses prototypically
inherit their parent (you already get those for free), and it could
break the intuitive behavior of using them with the private field
proposal to expose private properties to specific classes and
functions outside it. (Those functions would be created once per class
+ subclass, so it'd waste a *lot* of time for no otherwise good
reason.)

So given it's best to only run it once ever, and parameters don't make
sense, might as well just make it a declarative block.
-----

Isiah Meadows
me at isiahmeadows.com


On Thu, Jan 26, 2017 at 4:39 AM, T.J. Crowder
<tj.crowder at farsightsoftware.com> wrote:
> It's an interesting idea and certainly some other languages (Java,
> C#),  have static constructors; others (C++) have lived without. I
> don't recall the runtime details for C#, but certainly Java defers
> calling static constructors until actually initializing the class,
> which is indeed later than JavaScript's class definition time, at
> least for main scripts.
>
> Given modules, though, I wonder if static constructors of the style
> you're describing (where they aren't triggered until the first access
> to the class) are worth the overhead? Implementing would either mean
> trapping access to the class constructor's binding and doing init on
> first post-definition access (but I suspect that would be prone to
> being triggered early a lot), or effectively proxying the class
> constructor and using the `get` and some other traps (under the
> covers, granted, but the overhead is still there). That sounds heavy
> to me. Whereas using a module gives us the lazy init by the nature of
> modules -- if they're never imported, they're never run:
>
> ```js
> export default class MathLibrary {
>     // (Assumes the public class properties proposal)
>     static table = (() => {
>         const t = [];
>         // ...time-consuming work initializing t here...
>         return t;
>     })();
>
>     static calculate() {
>         // ...can use MathLibrary.table here...
>     }
> }
> ```
>
> (I wouldn't make `table` a static property at all -- exposes internal
> details of the class -- but I wanted to stay close to your example.)
>
> I wouldn't mind some syntactic sugar to make that prettier, but I'm
> not sure we need it to be deferred to first use of the class; I'm
> happy for it to run at class definition time. Using a module very
> nearly does that deferral already. The use case for deferred static
> construction becomes a module that imports the module above but then
> conditionally doesn't use it (and we only want to do the
> time-consuming work in the branch that does), which starts getting a
> bit...narrow?
>
> Putting modules to the side for now:
>
> From the current proposal:
>
>> The Singleton and Multiton patterns are two instances where this feature can be utilized.
>
> I assume you mean a singleton that isn't just a simple object, e.g., a
> class for which `new X` returns the same instance every time, or an
> object with a property or getter that always returns the same
> instance, with lazy initialization. Although a static constructor
> could be used for that, I'm not sure it brings a lot to the table vs.
> existing mechanisms, for instance:
>
> ```js
> let instance = null;
> class Singleton {
>     constructor() {
>         if (!instance) {
>             instance = this;
>             console.log("initialization");
>         }
>         return instance;
>     }
> }
>
> // Logs "initialization" during first construction, and not the second
> const s1 = new Singleton();
> const s2 = new Singleton();
> console.log(s1 === s2); // true
> ```
>
> or
>
> ```js
> let instance = null;
> const Singleton = {
>     get instance() {
>         if (!instance) {
>             instance = {}; // or whatever
>             console.log("initialization");
>         }
>         return instance;
>     }
> };
>
> // Logs "initialization" once, then logs true
> console.log(Singleton.instance === Singleton.instance);
> ```
>
> or
>
> ```js
> const Singleton = {
>     get instance() {
>         const instance = {}; // or whatever
>         console.log("initialization");
>         Object.defineProperty(Singleton, "instance", {
>             value: instance,
>             enumerable: true
>         });
>         return instance;
>     }
> };
>
> // Logs "initialization" once, then logs true
> console.log(Singleton.instance === Singleton.instance);
> ```
>
> Granted that last one is several lines, but if it's a pattern you use
> often, a helper function solves that.
>
> Similarly for Multiton. Can you elaborate how a static constructor applies?
>
> On `this` and static properties:
>
>>> [From Logan Smyth]
>>>
>>> Keep in mind that if something is a "static" in the ES6 class method
>>> sense, you will never be able to do `this.instances` to get it because the
>>> property does not live on `this`, it lives on the constructor.
>>
>> I'll have to think about that. Might need another proposal.
>
> More accurately, you *can* refer to static properties via `this`, but
> only from within static methods called in the normal way:
>
> ```js
> class Example {
>     // (Assumes public class fields proposal goes forward)
>     static a = 42;
>
>     static announce() {
>         console.log(`${Example.a}, ${this.a}`);
>     }
> }
>
> Example.announce();        // "42, 42"
> Example.announce.call({}); // "42, undefined"
> const a = Example.announce;
> a();                       // Error in strict mode
>                            // "42, undefined" (probably) in loose mode
> ```
>
> So for instance, you could have used `this` within your static
> `MathLibrary.Calculate` function. You can't from within a class
> constructor as in your current proposal text. (Side note: I'd
> recommend sticking to standard naming in these discussions -- e.g.,
> `calculate` rather than `Calculate` -- to avoid confusion.)
>
> In a method where `this` is an instance of the class, you can of
> course use `this.constructor.x` to refer to a static property `x`, if
> you want to avoid repeating the class name.
>
> Best,
>
> -- T.J.
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss


More information about the es-discuss mailing list