在es6中扩展单例类

问题描述 投票:1回答:3

我需要在JavaScript中扩展单例类。

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

我试图删除super以获取实例,但后来我收到了一个错误

必须在访问'this'或从派生构造函数返回之前调用派生类中的超级构造函数

代码示例:

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 oop es6-class
3个回答
2
投票

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

我试图删除超级以获取实例,但后来我遇到了错误

super()在创建的子类实例上调用父类的构造函数(即this引用的内容)。它不返回父类实例。 See here了解更多信息。

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


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

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

另一个是将参数放到单例类构造函数中。将参数传递给一个应该被实例化一次的类是没有意义的(你永远不会再对构造函数参数做任何事情)。您可以立即创建单例的参数属性。这是一个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());

请注意,Bwhoami继承A,并且构造函数调用计数器永远不会增加到1。

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


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" }

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

说实话,我认为你需要一些同一个类的实例,或者一个没有任何继承的简单对象文字。


0
投票

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

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

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();
© www.soinside.com 2019 - 2024. All rights reserved.