单例inheritanceBuggy行为
我已经发现在使用Singleton模式的JavaScript es6inheritance中的马车行为。
代码是 :
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
不知何故,遗产被搞砸了。 这里属性被覆盖而不是对象方法。
我的问题是为什么它正在工作(像现在扔),你能解释这种行为?
看来新对象需要全新的对象作为父对象(参见下面的解决scheme)。
如果你遇到同样的问题,这里是我的解决scheme:
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
你做的事有点奇怪 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
的第三个parameter passing的值)
你可以看到对于Two
类,没有新的对象被创build,并且Two.prototype
没有被使用。 相反, One
单例实例被使用和变异。
这是因为这一行:
if (instanceOne) return instanceOne;
One
构造函数在上面的代码中运行两次。 第二One
是super()
, this
是从Two.prototype
创build的,object是Two.prototype.method
。
super()
返回语句用One
单例replace,然后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
不一定要明确地返回。