'this'在构造函数和继承的构造函数中是不一样的

时间:2013-12-29 10:42:19

标签: javascript inheritance

我有这个简单的继承模式:

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相同?有解决方法吗?

3 个答案:

答案 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

同样的事情发生在p2p2.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 === p1p1.dist2 === p1p2.dist1 === p2p2.dist2 === p2。这里发生的是调用Emitter构造函数中的代码,这是创建的新Person对象。因此,当您在Emitter构造函数this.dis1 = this中说,this实际上是p1p2

在这个例子中没有使用原型的行,但它可以做你想要的其他很酷的事情:

  • 在Emitter.prottoype上声明的方法和属性可用于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之类的“本地”属性放在原型对象中。