在 es6 中扩展单例 class
extending singleton class in es6
我需要在 JavaScript 中扩展一个单例 class 。
我面临的问题是我获得了我正在扩展的 class 实例,而不是仅获得 class.
的方法
我试图删除 super
以不获取实例,但随后出现错误
Must call super constructor in derived class before accessing 'this' or returning from derived constructor
代码示例:
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)
在JavaScript中,单例只是一个对象字面量。
const a = {
options: {
i_am_a: "aaaaaa"
}
};
const b = {
options: {
i_am_b: "bbbbbb"
}
};
如果你真的需要一个构造函数,你可以只写一个returns一个对象的函数。
function makeSingleton(options) {
return {
options
}
}
const a = makeSingleton({i_am_a: "aaaaaa"});
const b = makeSingleton({i_am_b: "bbbbbb"});
这里没有继承链,只有两个对象字面量。如果您绝对需要 class,您可以创建一个,但这是不必要的资源浪费和输入。
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()
。
老实说,我认为您要么需要几个相同的实例 class,要么需要一个没有任何继承的简单对象文字。
好吧,我不知道这是否是最佳解决方案,但我所做的是检查构造函数名称是否与 class 名称不同。如果是这样,那么我让它创建一个新实例,因为这意味着我尝试扩展 class
这是我测试的一个工作示例
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();
所以,首先这里有一个误解:
I have tried to remove super to not get the instance but then I got an error
super()
在子 class 的已创建实例上调用父 class' 构造函数 (即 this
正在引用)。它不是 return 父 class 实例。 See here 获取更多信息。
因此,调用super()
根本不会违反父class 的单例属性。如果正确实施,它很可能只构建一次。
考虑到这一点,您应该稍微改进一下代码。
一个明智的改变是从构造函数中删除实例管理。一种解决方案是使用 静态构造函数 如果不存在实例则创建单例,或者 return 创建实例。
另一种方法是将参数丢给单例 class 构造函数。将参数传递给应该实例化一次的 class 真的没有意义(你永远不会再对构造函数参数做任何事情)。您可以立即创建单例的参数属性。这是 SO answer 支持这一点的 Java 单身人士。
带有静态构造函数且不带参数的完整示例如下所示:
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());
请注意 B
从 A
继承 whoami
并且构造函数调用计数器永远不会递增超过 1。
显然,使用这种方法,您不能保证单例 属性 对每个 class 成立,除非仅使用静态构造函数来生成实例(因为构造函数仍然可以访问)。我认为这是一个很好的妥协。
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);
我需要在 JavaScript 中扩展一个单例 class 。
我面临的问题是我获得了我正在扩展的 class 实例,而不是仅获得 class.
的方法我试图删除 super
以不获取实例,但随后出现错误
Must call super constructor in derived class before accessing 'this' or returning from derived constructor
代码示例:
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)
在JavaScript中,单例只是一个对象字面量。
const a = {
options: {
i_am_a: "aaaaaa"
}
};
const b = {
options: {
i_am_b: "bbbbbb"
}
};
如果你真的需要一个构造函数,你可以只写一个returns一个对象的函数。
function makeSingleton(options) {
return {
options
}
}
const a = makeSingleton({i_am_a: "aaaaaa"});
const b = makeSingleton({i_am_b: "bbbbbb"});
这里没有继承链,只有两个对象字面量。如果您绝对需要 class,您可以创建一个,但这是不必要的资源浪费和输入。
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()
。
老实说,我认为您要么需要几个相同的实例 class,要么需要一个没有任何继承的简单对象文字。
好吧,我不知道这是否是最佳解决方案,但我所做的是检查构造函数名称是否与 class 名称不同。如果是这样,那么我让它创建一个新实例,因为这意味着我尝试扩展 class
这是我测试的一个工作示例
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();
所以,首先这里有一个误解:
I have tried to remove super to not get the instance but then I got an error
super()
在子 class 的已创建实例上调用父 class' 构造函数 (即 this
正在引用)。它不是 return 父 class 实例。 See here 获取更多信息。
因此,调用super()
根本不会违反父class 的单例属性。如果正确实施,它很可能只构建一次。
考虑到这一点,您应该稍微改进一下代码。
一个明智的改变是从构造函数中删除实例管理。一种解决方案是使用 静态构造函数 如果不存在实例则创建单例,或者 return 创建实例。
另一种方法是将参数丢给单例 class 构造函数。将参数传递给应该实例化一次的 class 真的没有意义(你永远不会再对构造函数参数做任何事情)。您可以立即创建单例的参数属性。这是 SO answer 支持这一点的 Java 单身人士。
带有静态构造函数且不带参数的完整示例如下所示:
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());
请注意 B
从 A
继承 whoami
并且构造函数调用计数器永远不会递增超过 1。
显然,使用这种方法,您不能保证单例 属性 对每个 class 成立,除非仅使用静态构造函数来生成实例(因为构造函数仍然可以访问)。我认为这是一个很好的妥协。
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);