原型继承实际上与经典继承有何不同?


27

继承,多态性和封装是OOP的三个最不同的重要特征,从它们来看,继承如今具有很高的使用统计。我正在学习JavaScript,在这里,他们都说它具有原型继承,而且各地的人们都说它与经典继承大不相同

但是,从实际使用的角度来看,我不明白它们有什么区别?换句话说,当您定义基类(原型)然后从中派生一些子类时,您都可以访问基类的功能性对象,并且可以在派生类上扩展功能。如果我们认为我所说的是继承的预期结果,那么为什么我们要使用原型还是经典版本呢?

为了使自己更加清楚,我认为原型继承和经典继承的有用性和使用方式没有区别。这导致我没有兴趣去了解它们为何不同的原因,因为它们都导致同一件事,OOAD。原型继承实际上(不是理论上)与经典继承有何不同?

Answers:


6

关于JS OO的最新博客文章

我相信您的比较是JavaScript经典OO中的经典OO仿真,当然您看不到任何区别。

免责声明:将所有对“原型OO”的引用替换为“ JavaScript中的原型OO”。我不知道Self或任何其他实现的细节。

但是原型OO是不同的。对于原型,您只有对象,并且只能将对象注入其他对象原型链中。每当您访问对象上的属性时,都将搜索该对象以及原型链中的所有对象。

对于原型OO,没有封装的概念。封装是作用域,闭包和一流函数的功能,但与原型OO无关。也没有继承的概念,人们所谓的“继承”实际上只是多态性。

但是,它确实具有多态性。

例如

var Dog = {
  walk: function() { console.log("walks"); }
}

var d = Object.create(Dog);
d.walk();

显然d可以使用该方法,Dog.walk并且显示出多态性。

所以实际上有很大的不同。您只有多态性。

但是,如上所述,如果您愿意这样做(我不知道为什么这样做),您可以在JavaScript中模拟经典的OO,并可以访问(受限)封装和继承。


1
@Rayons,用于原型继承的 Google ,您会看到原型继承出现了。即使来自雅虎!
2011年

@Saeed继承是一个模糊的术语,通常被滥用。他们用“继承性”表示的意思是“多态性”。定义太含糊。
雷诺斯2011年

不,@ Rayons,我的意思是原型这个词是正确的术语,而不是原型。我没有谈论继承:)
Saeed Neamati 2011年

1
@Saeed是我错觉的错别字之一。我没有引起注意
雷诺斯(Raynos)2011年

1
嗯..术语。ick 我将javascript对象与其原型相关联的方式称为“委托”。在我看来,基于给定名称可能指向不同对象的不同代码的事实,多态性似乎是一个较低级别的问题。
肖恩·麦克米兰

15

古典继承从父类继承行为,没有任何状态。它继承了实例化对象时的行为。

原型继承从父对象继承行为和状态。它继承了对象被调用时的行为和状态。当父对象在运行时更改时,子对象的状态和行为会受到影响。

原型继承的“优势”是您可以在实例化所有对象之后“修补”状态和行为。例如,在Ext JS框架中,在实例化框架后,通常会加载对框架核心组件进行修补的“替代”。


4
在实践中,如果您要继承状态,那么您就要为遭受伤害的世界做好准备。如果继承状态驻留在另一个对象上,它将成为共享状态。
肖恩·麦克米兰

1
在我看来,在javascript中确实没有行为与状态分开的概念。除了构造函数外,所有行为都可以像创建其他任何状态一样分配/修改后对象创建。
Joeri Sebrechts 2012年

那么Python没有经典继承吗?如果我有class C(object): def m(self, x): return x*2,然后instance = C()当我跑步时,instance.m(3)我得到6。但是,如果我再改C这样C.m = lambda s, x: x*x,我跑instance.m(3)我现在得到9。如果我class D(C)在上更改一个方法也是如此,C那么任何实例都将D收到更改的方法。我是不是误会了,或者这是否意味着Python根据您的定义没有古典继承?
mVChr

@mVChr:在这种情况下,您仍在继承行为,而不是声明状态,这指向经典继承,但是它的确指向了我的定义“在实例化对象后继承行为”的问题。我不太确定如何更正定义。
Joeri Sebrechts

9

第一:大多数时候,您将使用对象,而不是定义它们,并且在两种范式下使用对象是相同的。

第二:大多数原型环境使用与基于类的环境相同的划分方式-实例上的可变数据,并继承了方法。因此,两者之间的差异很小。(请参阅我对此堆栈溢出问题的回答,以及无类自助论文组织程序。请参阅Citeseer以获得PDF版本。)

第三:Javascript的动态性质比继承的影响更大。我可以通过将类型的所有实例分配给基础对象来向其添加新方法,这一事实很简单,但是我可以通过重新打开该类在Ruby中执行相同的操作。

第四:实际差异很小,而忘记使用的实际问题new则大得多-也就是说,new与原型代码和经典代码之间的差异相比,丢失a的可能性更大。。

综上所述,原型继承与经典继承之间的实际区别在于,拥有方法(类)的事物与拥有数据(实例)的事物是相同的。这意味着您可以建立自己的类零碎地使用与在任何实例上使用的所有相同的对象操作工具。(实际上,这是所有类仿真库的工作方式。对于一种不太像其他所有方法的方法,请查看Traits.js)。如果您正在元编程,那么这主要是有趣的。


是的,最好的答案是IMO!
艾瓦尔(Aivar)2012年

0

JavaScript的原型继承在以下重要方面不同于类:

构造函数只是无需调用即可调用的函数new

function Circle (r, x, y) { 
  //stuff here
}
Var c = new Circle();
Circle.call(c, x, y, z); //This works and you can do it over and over again.

没有私有变量或方法,最多可以执行以下操作:

function Circle (r, x, y) {
  var color = 'red';
  function drawCircle () {
    //some code here with x, y and r
  }
  drawCircle();
  this.setX = function (x_) {
    x = x_;
    drawCircle();
  }

}
Circle.prototype.getX = function () {
   //Can't access x!
}

在前面的示例中,如果诉诸伪造的私有变量和方法,则无法有意义地扩展该类,此外,每次创建新实例时,都会重新创建您声明的任何公共方法。


您可以有意义地扩展“类”。你就不能访问本地变量colorrxydrawCircle绑定到的词汇范围,Circle
Raynos

确实可以,但是如果不创建每次添加到实例时都会创建的添加到上下文中的一堆“公共”方法,就无法做到这一点。
比约恩

您在这里使用的这是一个非常奇怪的模式。Circle.call()作为构造函数?似乎您正在尝试描述“功能对象”(用词不当...)
肖恩·麦克米兰

这不是我要做的事情,我只是说可以用JavaScript一遍又一遍地调用构造函数,但是不能用传统类的语言来调用。
比约恩

1
但是这些差异如何“重要”?我看不到通过调用函数来构造对象,而不是将“ new”用作“重要”,只是语法上的微小差异。同样,没有私有变量也没有根本不同,只是一个很小的差异。另外,按照您的描述,您可以拥有(类似)私有变量。
Roel 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.