Proposal for a static constructor and static member variables

T.J. Crowder tj.crowder at farsightsoftware.com
Thu Jan 26 09:39:00 UTC 2017


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.


More information about the es-discuss mailing list