繁体   English   中英

在 ES6 中扩展 singleton class

[英]extending singleton class in es6

我需要在 JavaScript 中扩展一个 singleton class。

我面临的问题是我得到了我正在扩展的 class 实例,而不是只获得 class 的方法。

我试图删除super以不获取实例,但后来出现错误

在访问“this”或从派生构造函数返回之前,必须在派生 class 中调用超级构造函数

代码示例:

 let instanceA = null; let instanceB = null; class A { constructor(options) { if (instanceA === null) { this.options = options; instanceA = this; } return instanceA; } } class B extends A { constructor(options) { if (instanceB === null) { super() console.log('real class is ' + this.constructor.name) this.options = options instanceB = this; } return instanceB; } } const a = new A({ i_am_a: "aaaaaa" }); const b = new B({ i_am_b: "bbbbbb" }) // this change a console.log(b.options) console.log(a.options)

所以,首先这里有一个误解:

我试图删除 super 以获取不到实例,但随后出现错误

super()在子类的创建实例上调用父类的构造函数(即this引用的是什么)。 它不返回父类实例。 请参阅此处了解更多信息。

因此,调用super()根本不会违反父类的单例属性。 如果正确实施,它很可能只构建一次。


考虑到这一点,您应该稍微改进您的代码。

一个明智的改变是从构造函数中删除实例管理。 一种解决方案是使用静态构造函数,如果不存在实例,则创建单例或返回创建的实例。

另一种方法是删除单例类构造函数的参数。 将参数传递给应该实例化一次的类实际上没有意义(你永远不会再对构造函数参数做任何事情)。 您可以立即创建单例的参数属性。 这是支持 Java 单例这一点的SO 答案

带有静态构造函数且不带参数的完整示例如下所示:

 let instanceA = null; let instanceB = null; let counters = { A: 0, B: 0 }; // count class instantiations class A { static getInstance() { if (instanceA === null) { instanceA = new A(); } return instanceA; } whoami() { const name = this.constructor.name; return `${name} #${counters[name]}`; } constructor() { counters[this.constructor.name] += 1; } } class B extends A { static getInstance() { if (instanceB === null) { instanceB = new B(); } return instanceB; } constructor() { super(); } } const a1 = A.getInstance(); const a2 = A.getInstance(); const a3 = A.getInstance(); const b1 = B.getInstance(); const b2 = B.getInstance(); const b3 = B.getInstance(); console.log(a1.whoami()); console.log(a2.whoami()); console.log(a3.whoami()); console.log(b1.whoami()); console.log(b2.whoami()); console.log(b3.whoami());

请注意, BA继承whoami并且构造函数调用计数器永远不会增加超过 1。

显然,使用这种方法,您不能保证每个类都具有单例属性,除非仅使用静态构造函数来生成实例(因为构造函数仍可访问)。 我认为这是一个很好的妥协。

在 JavaScript 中,单例只是一个对象字面量。

const a = {
    options: {
        i_am_a: "aaaaaa"
    }
};
const b = {
    options: {
        i_am_b: "bbbbbb"
    }
};

如果你真的需要一个构造函数,你可以只写一个返回一个对象的函数。

function makeSingleton(options) {

    return {
        options
    }

}

const a = makeSingleton({i_am_a: "aaaaaa"});
const b = makeSingleton({i_am_b: "bbbbbb"});

这里没有继承链,只有两个对象字面量。 如果你绝对需要一个类,你可以创建一个,但这是对资源和打字的不必要的浪费。

class Singleton {

    constructor(options) {
        this.options = options;
    }

}

const a = new Singleton({i_am_a: "aaaaaa"});
const b = new Singleton({i_am_b: "bbbbbb"});

在继承方面,如果这是您真正需要的东西,您可以根据需要使用Object.create()Object.assign() 请注意,两者都是浅层 - 它们仅在单层深工作,因此修改子项的options属性也会修改父项的options属性。

const a = {
    options: {
        i_am_a: "aaaaaa"
    },
    getOptions() {
        return this.options;
    }
};
const b = Object.create(a);
b.options.i_am_b: "bbbbbb";
a.options.i_am_b; // -> "bbbbbb"
b.getOptions(); // -> { i_am_a: "aaaaaa", i_am_b: "bbbbbb" }

当然,您也可以在options上使用Object.create()Object.assign()

老实说,我认为您要么需要同一个类的几个实例,要么需要一个没有任何继承的简单对象字面量。

好吧,我不知道这是否是最好的解决方案,但我所做的是检查构造函数名称是否与类名称不同。 如果是这样,那么我让它创建一个新实例,因为这意味着我尝试扩展该类

这是我的测试的一个工作示例

 let instanceA = null; let instanceB = null; class A { constructor(options) { this.options = options; if (instanceA === null) { instanceA = this; } if(this.constructor.name !== "A"){ return this; } return instanceA; } method1(){ console.log(this.constructor.name) } } class B extends A { constructor(options) { if (instanceB === null) { super(options) instanceB = this; } return instanceB; } } const a = new A({ i_am_a: "aaaaaa" });a const b = new B({ i_am_b: "bbbbbb" }) const c = new A({ i_am_c: "ccccc" }); const d = new B({ i_am_d: "ddddd" }) console.log(a.options) console.log(b.options) console.log(c.options) console.log(d.options) a.method1(); b.method1(); c.method1(); d.method1();

 const instances = {} class Singleton { constructor() { const instance = instances[this.constructor]; if (instance == null) { return instances[this.constructor] = this; } return instance; } } class Foo extends Singleton { constructor() { super(); this.foo = "foo"; } } class Bar extends Singleton { constructor() { super(); this.foo = "bar"; } } const foo1 = new Foo(); const foo2 = new Foo(); const bar1 = new Bar(); const bar2 = new Bar(); console.log(foo1 === foo2, bar1 === bar2, foo1 === bar1, foo1.foo = 123, foo2, bar1);

要通过扩展 singleton 来实现您的模式,您可以使用代理:

 const proxify = (what) => new Proxy(what, { instance: null, construct(B, args) { return this.instance??= new what(...args); } }); const A = proxify(class { #a = 1 c = 0 }); const B = proxify(class extends A { #b = 2 d = 0 }); // make sure we have a singleton for A const a1 = new A; const a2 = new A; a2.c = 1; console.log(a1.c, a2.c); // make sure we have a singleton for B const b1 = new B; const b2 = new B; b1.c = 2; b2.d = 3; console.log(b1.c, b2.c); console.log(b1.d, b2.d);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM