1

我在使用单例模式的 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

4

2 回答 2

2

发生这种情况是因为这一行:

    if (instanceOne) return instanceOne;

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

从用单例super()替换的返回语句,然后构造函数只修改单例实例。thisOneTwoOne

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

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不必显式返回。

于 2016-10-27T14:07:45.100 回答
1

你正在做一些有点奇怪的事情。ecmascript 6 中的构造函数和子类并不像您认为的那样工作。您可能希望阅读这篇博文(尤其是第 4 节)以了解更多信息。

从那篇文章中可以看出,您的代码在后台如下所示:

let instanceOne = null;

function One() {
//  var this = Object.create(new.target.prototype);  // under the hood

    if (instanceOne) return instanceOne;

    this.name = 'one';
    instanceOne = this;
    return instanceOne;
}
One.prototype.method = function() { console.log('Method in one'); }

let instanceTwo = null;

function Two() {
    var that = undefined;

    that = Reflect.construct(One, [], new.target);

    if (instanceTwo) return instanceTwo;

    that.name = 'two';
    instanceTwo = that;
    return instanceTwo;
}
Two.prototype.method = function() { console.log('Method in two'); }
Object.setPrototypeOf(Two, One);
Object.setPrototypeOf(Two.prototype, One.prototype);

const objOne = Reflect.construct(One, [], One);
const objTwo = Reflect.construct(Two, [], Two);

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

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

您可以看到,对于Two该类,没有创建新对象,Two.prototype也没有使用它。相反,One单例实例被使用和变异。

于 2016-10-27T14:15:38.823 回答