Let’s say you want to write a constructor that produces instances that are frozen (immutable). One problem, you have to solve, is: when do you freeze this? If you always – unconditionally – perform the operation in the constructor then you can’t create sub-constructors that have their own instance properties. This blog post explains how to work around this problem.
function Point(x, y) { this.x = x; this.y = y; Object.freeze(this); }But now you can’t write a sub-constructor [1] ColorPoint:
function ColorPoint(x, y, color) { Point.call(this, x, y); this.color = color; // Impossible, `this` is frozen }You want super-constructors to initialize their part of an instance before their sub-constructors, so calling super-constructors last is a non-solution.
function Point(x, y) { this.x = x; this.y = y; if (this.constructor === Point) { Object.freeze(this); } } Point.prototype.toString = function () { return this.x + ' ' + this.y; };ColorPoint can be implemented in a similar manner:
function ColorPoint(x, y, color) { Point.call(this, x, y); this.color = color; if (this.constructor === ColorPoint) { Object.freeze(this); } } ColorPoint.prototype = Object.create(Point); ColorPoint.prototype.constructor = ColorPoint; ColorPoint.prototype.toString = function () { return Point.prototype.toString.call(this) + ' ('+this.color+')'; };
Point.prototype.maybeFreeze = function (constr) { if (this.constructor === constr) { Object.freeze(this); } };Then Point and ColorPoint look loke this:
function Point(x, y) { this.x = x; this.y = y; this.maybeFreeze(Point); } function ColorPoint(x, y, color) { Point.call(this, x, y); this.color = color; this.maybeFreeze(ColorPoint); }One could even move maybeFreeze to a helper prototype for all freezing constructors (e.g. FrozenObject.prototype):
function Point(x, y) { ... } Point.prototype = Object.create(FrozenObject.prototype); ...Obviously, there is no need to change ColorPoint.
var Point = function me(x, y) { this.x = x; this.y = y; this.afterConstructor(me); };
Point.prototype.maybeFreeze = function (constr) { if (Object.getPrototypeOf(this) === constr.prototype) { Object.freeze(this); } };Note that checking via the constructor property also relies on the instance prototype. For example, it must have the property correctly set up.