【问题标题】:Understanding prototype inheritance了解原型继承
【发布时间】:2016-10-31 06:08:28
【问题描述】:

我绘制了下图来演示对象是如何继承的(函数构造函数标记为蓝色,从这些构造函数创建的对象标记为绿色):

这是创建这种层次结构的代码:

function Figure() {}

function Rect() {}
Rect.prototype = new Figure();

function Square() {}
Square.prototype = new Rect();

function Ellipse() {}
Ellipse.prototype = new Figure();

function Circle() {}
Circle.prototype = new Ellipse();

现在我想检查new Square() 是否继承自Rect,所以这是我希望JavaScript 引擎检查它的方式:

var s = new Square();
s instanceof Rect // ?

s.__proto__ === Rect.prototype // false
new Rect()      new Figure()

s.__proto__.__proto__ === Rect.prototype // true
new Figure()              new Figure()

所以s instanceof Rect 应该返回true。这是预期的,实际上是我运行代码时返回的内容。但是后来我想检查new Circle()是否继承自Rect,所以我遵循相同的逻辑:

var c = new Circle();
c instanceof Rect // ?

c.__proto__ === Rect.prototype // false
new Ellipse()      new Figure()

c.__proto__.__proto__ === Rect.prototype // true
new Figure()              new Figure()

因此,使用此检查逻辑 c instanceof Rect 应该返回 true,但如果我实际运行代码,c instanceof Rect 返回 false。我是不是对instanceof操作符的机制有误解?

【问题讨论】:

  • 您能否在您的绘图中包含“对象”,以便我了解您认为箭头的走向?
  • JS 具有 prototype 驱动的结构,不是真正的多级类继承方案,典型的其他语言。每个对象都有一个指向 原型的隐含指针,这形成了一个单链接链。 Mozilla 基金会在developer.mozilla.org/en-US/docs/Web/JavaScript/… 上写了一篇很好的描述性文章,介绍了这个方案是什么,以及它不是。本文还讨论了 JS-vs.ECMA 和语言版本的差异。
  • @ChrisClark,object 在右边,是Figure.prototype
  • 我认为您应该首先在此处包含您的函数定义。
  • @MikeRobinson,我读过很多文章,这个问题是关于一个特别配置的继承链及其带来的混乱

标签: javascript inheritance


【解决方案1】:

Circles 和 Squares 在它们的原型链中都有一个 Figure 的事实意味着 c instanceof Figures instanceof Figure 都是正确的,但它们都有自己的 Figure,因为 new Figure() 被调用了两次。请注意new Figure() != new Figure(),因为 Javascript 中的对象永远不会等于其他对象。这是对图表的更新,显示了上面代码中实际发生的情况。您可以看到 Circles 和 Squares 在其原型链中各有一个不同的 Figure:

请注意,您可以稍微更改代码,以便重复使用一个图形作为 Rect 和 Ellipse 的原型,这将等同于您原始帖子中的图表,并会产生您期望看到的行为:

function Figure() {}
var f = new Figure();

function Rect() {}
Rect.prototype = f;

function Square() {}
Square.prototype = new Rect();

function Ellipse() {}
Ellipse.prototype = f;

function Circle() {}
Circle.prototype = new Ellipse();

console.log( (new Circle) instanceof Rect ); // true

【讨论】:

    【解决方案2】:

    您的逻辑是正确的,但最初的假设有点错误。可以使用原型模拟常规的基于类的继承。

    为了重现您为我们绘制的结构,我创建了以下代码:

    function Figure() {}
    function Rect() {}
    function Square() {}
    
    function Ellipse() {}
    function Circle() {}
    
    Ellipse.prototype = Rect.prototype = new Figure();
    
    Square.prototype = new Rect();
    Circle.prototype = new Ellipse();
    
    console.log("is Figure: " + (new Circle() instanceof Figure));
    console.log("is Ellipse: " + (new Circle() instanceof Ellipse));
    console.log("is Rect: " + (new Circle() instanceof Rect));

    如您所见,new Circle() instanceof Rect 在您配置时返回 true。问题是通过将Ellipse.prototypeRect.prototype设置为同一个对象,它们基本上变成了同一个类型(有多个构造函数)。

    那么你如何解决它?为原型创建不同的Figure 实例,如下所示:

    function Figure() {}
    function Rect() {}
    function Square() {}
    
    function Ellipse() {}
    function Circle() {}
    
    Ellipse.prototype = new Figure();
    Rect.prototype = new Figure();
    
    Square.prototype = new Rect();
    Circle.prototype = new Ellipse();
    
    console.log("is Figure: " + (new Circle() instanceof Figure));
    console.log("is Ellipse: " + (new Circle() instanceof Ellipse));
    console.log("is Rect: " + (new Circle() instanceof Rect));

    而现在的结果就是大家所期待的。

    编辑

    我已经重绘了你的图片,并根据你的文本示例绘制了另一张图片,它说明了对象的真实情况,这与我的第二个代码相同。

    原始的:我突出显示了表达式Rect.prototype === new Circle().__proto__.__proto__中的引用:

    第二个:

    附言

    2016 年的今天,不是Circle.prototype = new Ellipse() 是你应该实现继承的方式,而是使用标准的类继承:

    class Figure {}
    class Rect extends Figure {}
    class Square extends Rect {}
    class Ellipse extends Figure {}
    class Circle extends Ellipse {}
    
    console.log("new Circle is Figure: " + (new Circle() instanceof Figure));
    console.log("new Circle is Rect: " + (new Circle() instanceof Rect));

    【讨论】:

    • 另一种方法是use Object.create
    • 如果我运行我的代码,我也会得到console.log("is Rect: " + (new Circle() instanceof Rect)); 等于false。我的问题是我的误解在 instanceof 运算符的检查算法的机制上
    • @nem035:事实上,这不仅仅是“另一种”方式,而是唯一正确的方式:-)
    • @nem035 @Bergi 我完全同意。这个例子只是为了演示区别,而不是关于实现继承的完整教程。那里有很多,我看不出在Object.create创建的原型对象上正确设置constructor属性的意义。
    【解决方案3】:

    鉴于您问题中的示例代码,c.__proto__.__proto__ === Rect.prototype 返回falsenew Figure 被调用两次,并创建了两个不同的 Figure 实例。

    不同的对象实例不相等。

    function Figure() {}
    
    function Rect() {}
    Rect.prototype = new Figure();
    
    function Square() {}
    Square.prototype = new Rect();
    
    function Ellipse() {}
    Ellipse.prototype = new Figure();
    
    function Circle() {}
    Circle.prototype = new Ellipse();
    
    var c = new Circle();
    
    console.log('c instanceof Rect:', c instanceof Rect);
    
    console.log('c.__proto__ === Rect.prototype', c.__proto__ === Rect.prototype);
    
    console.log('c.__proto__.__proto__ === Rect.prototype', c.__proto__.__proto__ === Rect.prototype);

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-27
      • 2016-07-30
      • 1970-01-01
      • 1970-01-01
      • 2015-07-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多