Singleton继承Buggy行为

时间:2016-10-27 13:18:13

标签: node.js ecmascript-6 extends es6-class

我在使用Singleton模式的javascript es6继承中发现了错误行为。

代码

let instanceOne = null;

class One {
    constructor() {
        if (instanceOne) return instanceOne;

        this.name = 'one';
        instanceOne = this;
        return instanceOne;
    }

    method() {
        console.log('Method in one');
    }
}


let instanceTwo = null;

class Two extends One {
    constructor() {
        super();

        if (instanceTwo) return instanceTwo;

        this.name = 'two';
        instanceTwo = this;
        return instanceTwo;
    }

    method() {
        console.log('Method in two');
    }
}

const objOne = new One();
const objTwo = new Two();

console.log(objOne.name);
console.log(objTwo.name);
objOne.method();
objTwo.method();

显示

two
two
Method in one
Method in one

继承以某种方式搞砸了。这里的属性被覆盖,但不是对象方法。

我的问题是为什么它会起作用(比如现在抛出)并且你能解释一下这种行为吗?

看来新对象需要全新的对象作为父对象(请参阅下面的解决方案)。

如果您遇到同样的问题,这是我的解决方案:

let instanceOne = null;

class One {
    constructor(brandNewInstance = false) {
        if (instanceOne && !brandNewInstance) return instanceOne;

        this.name = 'one';

        if (brandNewInstance) return this;

        instanceOne = this;
        return instanceOne;
    }

    method() {
        console.log('Method in one');
    }
}


let instanceTwo = null;

class Two extends One {
    constructor() {
        super(true);

        if (instanceTwo) return instanceTwo;

        this.name = 'two';
        instanceTwo = this;
        return instanceTwo;
    }

    method() {
        console.log('Method in two');
    }
}

我使用node.js v6.9.1

2 个答案:

答案 0 :(得分:1)

这是因为这一行:

    if (instanceOne) return instanceOne;

One构造函数在上面的代码中运行两次。第二个One来电是super(),在这种情况下,this是从Two.prototype创建的,对象方法是Two.prototype.method

super()替换this并使用One单例返回语句,然后Two构造函数只修改One单例实例。

可以使用静态属性来保存实例:

constructor() {
    if (this.constructor.hasOwnProperty('instance'))
        return this.constructor.instance;

    this.constructor.instance = this;

    this.name = 'one';
}

或者,如果与子类共享实例是预期的行为,

constructor() {
    if ('instance' in this.constructor)
        return this.constructor.instance;

    this.name = 'one';
    this.constructor.instance = this;
}

在这种情况下,所有单例机制都由One构造函数完成,Two只需要调用super

constructor() {
    super();

    this.name = 'two';
}

此外,结束return语句是多余的。 this不必明确返回。

答案 1 :(得分:1)

你做的事情有点奇怪。 ecmascript 6中的构造函数和子类不会以您认为的方式工作。您可能希望阅读this blog post(特别是第4节)以了解更多信息。

从那篇文章中可以看出,你的代码看起来就像这样:

Reflect.construct

(new.target是作为Two的第三个参数传递的值)

您可以看到Two.prototype类没有创建新对象,并且未使用One。相反,"DOMPDF_ENABLE_JAVASCRIPT" => true单例实例被使用和变异。