我有这个简单的继承模式:
function Emitter() {
this.dis1 = this;
};
function Person() {
this.dis2 = this;
};
Person.prototype = new Emitter();
var p1 = new Person();
var p2 = new Person();
console.log(p1.dis1 === p2.dis1); //true - why?
console.log(p1.dis2 === p2.dis2); //false
为什么单独对象的dis1相同?有解决方法吗?
答案 0 :(得分:3)
关于new
operator如何运作的说明:
Foo = function(){};
Foo.prototype;
var bar = new Foo();
最后一行发生的事情是创建一个继承自Foo.prototype
的新对象,然后执行Foo
函数中的代码,this
作为新对象。然后bar被初始化为该新对象。
你的继承模型有几个缺陷,你已经发现了其中一个。所有缺陷都来自这一行代码:
Person.prototype = new Emitter();
这一行表示所有Person实例继承的对象是Emitter的实例(在那里创建的Emitter实例)。
p1.dist1
查找原型链上的dist1属性。首先,它会在p1
中查看它是否具有dist1
属性,但它没有。接下来,它会在p1.[[Prototype]]
中查找已初始化为Person.prototype
的内容,它会找到dist1
属性,其值为Person.prototype
(上面提到的Emitter的一个实例)。
p1.dist
执行相同操作,但该属性位于p1
,并且等于p1
。
同样的事情发生在p2
,p2.dist1
是Emitter的同一个实例,而p2.dist2
等于p2
。
进行继承并避免所有这些问题的方法是:
function Emitter() {
this.dis1 = this;
};
function Person() {
Emitter.call(this); // calls the parent constructor.
this.dis2 = this;
};
Person.prototype = Object.create(Emitter.prototype); // you don't even use this line
var p1 = new Person();
var p2 = new Person();
在此模型中,p1.dist1 === p1
和p1.dist2 === p1
,p2.dist1 === p2
和p2.dist2 === p2
。这里发生的是调用Emitter构造函数中的代码,这是创建的新Person对象。因此,当您在Emitter构造函数this.dis1 = this
中说,this
实际上是p1
或p2
。
在这个例子中没有使用原型的行,但它可以做你想要的其他很酷的事情:
p1 instanceof Emitter
将是真的。您可以在MDN上找到此模型。
答案 1 :(得分:2)
dis1
属性为looked up in the prototype chain。
由于它不在p1或p2中,因此它会在原型中查找,原型是您使用new Emitter()
创建的对象。这就是你拥有相同价值的原因。
JavaScript中的原型模型与其他语言(如Java)中的继承模型非常不同。小心不要尝试在JavaScript中模拟Java或C ++设计:这总会导致糟糕的设计。
现在,假设您希望在Emitter级别拥有一个对象,并且每个Person实例都有不同的对象,那么最简单的解决方案可能是按需初始化值或使用特定函数(您可以使用约定来命名它{ {1}}然后覆盖它)。
按需初始化的示例:
initialize
事实上,这里的最佳解决方案可能是(通常在真实的OOP语言中)prefer composition over inheritance,并且只需将发射器设置为人的属性。
答案 2 :(得分:1)
正如已经提到的破坏,dis1
在原型链中被抬起。如果您希望针对不同的dis1
使用不同的Person
,则可以使用以下模式。
function Emitter() {
this.dis1 = this;
};
function Person() {
Emitter.call(this);
this.dis2 = this;
};
Person.prototype = Object.create(Emitter.prototype);
var p1 = new Person();
var p2 = new Person();
console.log(p1.dis1 === p2.dis1); //false
console.log(p1.dis2 === p2.dis2); //false
请注意,您必须在Emitter
构造函数中调用Person
,同时提供this
作为当前上下文。现在,创建一个新Person将创建一个对象,该对象包含Person
的所有(本地)属性以及平面层次结构中Emitter
的所有(本地)属性:
{
dis1: Person,
dis2: Person
}
您还可以使用Object.create
来实现原型继承。这样做的好处是,您实际上并没有调用Emitter
构造函数,因此您不会将dis1
之类的“本地”属性放在原型对象中。