Darien Valentine (2017-09-11T08:29:28.000Z)
valentinium at gmail.com (2017-09-11T08:31:56.340Z)
I use the WeakMap approach, too. Recently I find myself writing classes where the class has a corresponding WeakMap holding the "shadow instances" (as opposed to having one WM per property): const PRIV = new WeakMap(); class Foo { constructor() { PRIV.set(this, { bar: 0, /*...other private state init...*/ }); } get bar() { return PRIV.get(this).bar; } set bar(val) { if (!Number.isInteger(val)) throw new TypeError('no!'); PRIV.get(this).bar = val; } } I only do this for classes that are part of some public interface, where I want finer control over what state is exposed and wish to ensure that the object cannot enter an invalid state; for internal stuff it’d probably be overkill. The property-that-redefines-itself approach makes me uncomfortable because I don’t want property access to have observable side effects from the consumer side. const foo = new ClassWithThatPattern; foo.hasOwnProperty('bar'); // false foo.bar; foo.hasOwnProperty('bar'); // true In any case ... re: lazy initialization, I would agree that decorators represent a perfect way to make this pattern declarative & expressive. I suppose the private instance properties aspect of the class properties proposal, now at stage 3, also provides a way to reduce boilerplate by a bit, but not to the same degree. (I’d second kaizu’s opinion that the example of lazy init of a db seems kind of iffy, at least for node apps, where you kinda want to know your db is working before you even init the rest of the app, as failure almost invariably represents a terminal condition — but that said, it’s just an example, and there are certainly cases where lazy init of properties is worthwhile, e.g. when you have very large collections of many small instances and only an unknown-in-advance subset will actually need such-and-such properties calculated ultimately.)