为什么原型继承子类是另一个子类的实例?

时间:2011-10-27 15:53:50

标签: javascript

也许我做错了。我想设置一个基类和两个继承自该类的类。然而,它告诉我,一个孩子是另一个孩子的一个例子......这可能不正确,可以吗?我在这做错了什么?

function BaseClass(){}
function Child1(){}

Child1.prototype = BaseClass.prototype;
Child1.prototype.constructor = Child1;

function Child2(){}
Child2.prototype = BaseClass.prototype;
Child2.prototype.constructor = Child2;

console.log((new Child1() instanceof Child2)); // true

4 个答案:

答案 0 :(得分:3)

您已将两个类的原型设置为完全相同的对象(“BaseClass.prototype”),并将其上的“constructor”属性设置为“Child2”。换句话说,“Child1.prototype”和“Child2.prototype”是相同的,因此代码中只涉及一个“构造函数”属性,最终它是“Child2”。

也许您想将这些原型设置为BaseClass的实例

Child1.prototype = new BaseClass();
Child1.prototype.constructor = Child1;

Child2.prototype = new BaseClass(); // a different object
Child2.prototype.constructor = Child2;

(@ Raynos指出实例化这样的原型值可能会导致细微的问题,所以你可能更喜欢在他的答案中使用这种方法。)

答案 1 :(得分:2)

Child1.prototype = BaseClass.prototype;

应为Child1.prototype = Object.create(BaseClass.prototype)

您不希望原型对象相同,您想要一个继承自BaseClass的新原型对象

Live Example

免责声明: Object.create是ES5,使用ES5-shim进行旧版平台支持。

为您提供更全面的示例:

// Create base prototype
var Base = {
  method: function () {
    return this.things;
  },
  constructor: function (things) {
    this.things = things;
  }
};
// Link constructor and prototype
Base.constructor.prototype = Base;

// Create child prototype that inherits from Base
var Child = Object.create(Base);
// overwrite constructor
Child.constructor = function (things) {
  things++;
  Base.constructor.call(things);
}
// Link constructor and prototype
Child.constructor.prototype = Child;

// Swap prototype and constructor around to support new
ChildFactory = Child.constructor;

var c = new ChildFactory(41);
console.log(c.method()); // 42

<强>目视:

var Base = { ... }

这里我们简单地用方法和属性创建一个对象。我们希望能够创建此对象的实例。所以Base是一个“类”,可以从实例(包括constructor)访问它的所有方法和属性。

Base.constructor.prototype = Base;

您需要链接“构造函数”,这是一个可以使用new调用的函数,它将为您提供对象ConstructorFunction.prototype的新实例以及原型对象。这基本上是你需要手动完成的一些粘合剂,因为ES不会为你做这件事。

如果您忘记这样做,那么构造函数(X.constructor)没有.prototype属性来使实例继承。

var Child = Object.create(Base);

创建[[Prototype]]Base的新对象。这基本上是原型链的开始

Child -> ([[Prototype]] = Base) -> ([[Prototype]] = Object.prototype) -> null

Child.x = ...

现在我们向子对象添加属性,这些属性是Child将继承的方法实例。基本上我们正在创建一个新的原型对象来继承。所有实例都将共享方法,并在原型链中共享方法(包括Base)。

ChildFactory = Child.constructor;

new关键字只适用于构造函数而不是原型对象,所以我们基本上要说“将我们的原型对象变量切换到构造函数变量”

就个人建立“类”时,我发现原型对象非常适合直接处理,在创建实例时,我发现构造函数很难处理。

现在我们致电var c = new Child(41);

它将调用我们定义的构造函数,它将递增things,然后调用基础构造函数。

此时请注意c的原型链看起来像

c -> ([[Prototype]] = Child) 
  -> ([[Prototype]] = Base) 
  -> ([[Prototype]] = Object.prototype) 
  -> null

答案 2 :(得分:1)

如果你分配给原型,那是不是意味着你完全覆盖它?因此Child1和Child2实际上变成了BaseClass对象,而不是子类。

另见http://www.zipcon.net/~swhite/docs/computers/languages/object_oriented_JS/inheritance.html。 JS中适当的OO需要付出相当大的努力。

答案 3 :(得分:1)

这并不意味着检查对象new Child1()是否由“Child1”构造函数创建。 instanceof运算符只接受一个对象原型 - (new Child1()).[[Prototype]]并检查它在原型链中的存在,主题为“Child1.prototype”的检查。通过构造函数的内部[[HasInstance]]方法激活运算符instanceof。 因此,您需要以下列方式更改代码:

function BaseClass(){}
function Child1(){}

Child1.prototype = new BaseClass();
Child1.prototype.constructor = Child1;

function Child2(){}
Child2.prototype = new BaseClass();
Child2.prototype.constructor = Child2;
console.log((new Child1() instanceof Child2)); // false

这将是在javascript中正确实现OOP