Panda Noir

JavaScript の限界を究めるブログでした。最近は幅広めに書いてます。

同じ引数のとき新しいインスタンスを生成しない省エネなClassを作る

タイトルはつまり new Hoge(1, 2, 3) === new Hoge(1, 2, 3)を実現するということです。

実装

まず実装をお見せします。

const pool = [];
class Hoge {
    constructor(x, y, z) {
        if (!pool[x]) pool[x] = [];
        if (!pool[x][y]) pool[x][y] = [];
        if (pool[x][y][z]) return pool[x][y][z];
        this.x = x;
        this.y = y;
        this.z = z;
        pool[x][y][z] = this;
    }
};

解説

ポイントはコンストラクタがオブジェクトを返している点です。コンストラクタがreturnをして、返り値がObject型のときはそのオブジェクトが返り値になります。*1

つまり、pool[x][y][z]が存在していた場合は新しいインスタンスは生成されずpool[x][y][z]が返されます。そのため、冒頭でお話したnew Hoge(1, 2, 3) === new Hoge(1, 2, 3)が実現できます。

注意点

この手法には問題点があります。それはプロパティが共有されてしまうことです。(同じ値を指しているのですから当然ではあります)

const a = new Hoge(1, 2, 3);
a.w = 4;
const b = new Hoge(1, 2, 3);
assert.equal(b.w, 4); // true!!!

このように片方で起きた変更がそのまま受け継がれるので、意図しない挙動になりやすいです。そのため、イミュータブルなインスタンスを生成するときくらいしか使いみちがありません。

*1:returnがない、もしくは返り値がプリミティブ型であったら、生成したインスタンスが返り値になります