javascript中的经典继承与原型继承
Posted
技术标签:
【中文标题】javascript中的经典继承与原型继承【英文标题】:classical inheritance vs prototypal inheritance in javascript 【发布时间】:2013-11-07 03:56:34 【问题描述】:我在 Google 上搜索了这么多链接,但对经典继承和原型继承之间的区别不太了解?
我从中学到了一些东西,但我仍然对这些概念感到困惑。
Benefits of prototypal inheritance over classical?
http://aaditmshah.github.io/why-prototypal-inheritance-matters/
经典继承
// Shape - superclass
function Shape()
this.x = 0;
this.y = 0;
//superclass method
Shape.prototype.move = function(x, y)
this.x += x;
this.y += y;
console.info("Shape moved.");
;
// Rectangle - subclass
function Rectangle()
Shape.call(this); //call super constructor.
//subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
经典继承内部是否使用原型继承?
http://aaditmshah.github.io/why-prototypal-inheritance-matters/
从上面的链接中,我了解到我们不能在运行时在经典继承中添加新方法。它是否正确?但是你可以查看上面的代码我可以通过原型在运行时添加“move”方法和任何方法。那么这是基于原型的经典继承吗?如果是这样,什么是实际的经典继承和原型继承?我对此感到困惑。
原型继承。
function Circle(radius)
this.radius = radius;
Circle.prototype.area = function ()
var radius = this.radius;
return Math.PI * radius * radius;
;
Circle.prototype.circumference: function ()
return 2 * Math.PI * this.radius;
;
var circle = new Circle(5);
var circle2 = new Circle(10);
这类似于经典继承吗?我对什么是原型继承感到完全困惑?什么是经典继承?为什么经典继承不好?
你能给我一个简单的例子,以便以简单的方式更好地理解这些。
谢谢,
湿婆
【问题讨论】:
复制,看看这个:***.com/questions/1595611/… 不知道你在说什么 - 第一个代码块是原型继承,不是经典的。您的第二个代码块根本没有继承! 这可能很简单:blog.stephenwyattbush.com/2012/05/01/… @alnitak developer.mozilla.org/en-US/docs/Web/javascript/Reference/… 这个链接告诉我们一个是经典继承。这就是为什么我很困惑。 要详细了解为什么要避免经典继承,请参阅我的演讲“经典继承已过时:如何在原型 OO 中思考”vimeo.com/69255635 【参考方案1】:您在问题中演示的两个代码示例都使用了原型继承。事实上,你用 JavaScript 编写的任何面向对象的代码都是原型继承的范例。 JavaScript 根本没有经典继承。这应该有点清楚了:
Inheritance
|
+-----------------------------+
| |
v v
Prototypal Classical
|
+------------------------------+
| |
v v
Prototypal Pattern Constructor Pattern
如您所见,原型继承和经典继承是两种不同的继承范式。一些语言,如 Self、Lua 和 JavaScript 支持原型继承。但是,大多数语言(如 C++、Java 和 C#)都支持经典继承。
面向对象编程的快速概述
原型继承和经典继承都是面向对象的编程范例(即它们处理对象)。对象只是封装了真实世界实体属性的抽象(即它们代表程序中的真实事物)。这称为抽象。
抽象:现实世界事物在计算机程序中的表示。
理论上,抽象定义为“从特定示例中提取共同特征形成的一般概念”。然而,为了便于解释,我们将使用上述定义。
现在有些对象有很多共同点。例如,泥地自行车和哈雷戴维森有很多共同点。
一辆泥地自行车:
哈雷戴维森:
泥泞自行车和哈雷戴维森都是自行车。因此,自行车是泥泞自行车和哈雷戴维森的概括。
Bike
|
+---------------------------------+
| |
v v
Mud Bike Harley Davidson
在上面的例子中,自行车、泥地自行车和哈雷戴维森都是抽象的。然而,自行车是泥泞自行车和哈雷戴维森的更一般的抽象(即泥泞自行车和哈雷戴维森都是特定类型的自行车)。
泛化:更具体抽象的抽象。
在面向对象的编程中,我们创建对象(它们是现实世界实体的抽象),我们使用类或原型来创建这些对象的泛化。泛化是通过继承创建的。自行车是泥地自行车的概括。因此泥地自行车继承自自行车。
经典的面向对象编程
在经典的面向对象编程中,我们有两种抽象类型:类和对象。如前所述,对象是现实世界实体的抽象。另一方面,类是对象或另一个类的抽象(即它是泛化)。例如,考虑:
+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | Man | Class of object johnDoe. |
| 3 | Human | Superclass of class Man. |
+----------------------+----------------+---------------------------------------+
正如您在经典的面向对象编程语言中所见,对象只是抽象(即所有对象的抽象级别都为 1),而类只是泛化(即所有类的抽象级别都大于 1)。
经典的面向对象编程语言中的对象只能通过实例化类来创建:
class Human
// ...
class Man extends Human
// ...
Man johnDoe = new Man();
总而言之,在经典的面向对象编程语言中,对象是现实世界实体的抽象,类是泛化(即对象或其他类的抽象)。
因此,随着抽象级别的增加,实体变得更加通用,而随着抽象级别的降低,实体变得更加具体。从这个意义上说,抽象级别类似于从更具体的实体到更一般的实体的范围。
原型面向对象编程
原型面向对象编程语言比经典的面向对象编程语言简单得多,因为在原型面向对象编程中我们只有一种抽象类型(即对象)。例如,考虑:
+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | man | Prototype of object johnDoe. |
| 3 | human | Prototype of object man. |
+----------------------+----------------+---------------------------------------+
正如您在原型面向对象编程语言中看到的那样,对象是现实世界实体(在这种情况下它们被简单地称为对象)或其他对象(在这种情况下它们被称为它们抽象的那些对象的原型)的抽象。 .因此,原型是一种概括。
原型的面向对象编程语言中的对象可以是从无到有(即无中生有)或从另一个对象(成为新创建对象的原型)创建的:
var human = ;
var man = Object.create(human);
var johnDoe = Object.create(man);
在我看来,原型面向对象编程语言比经典的面向对象编程语言更强大,因为:
-
只有一种抽象类型。
概括只是对象。
现在您一定已经意识到经典继承和原型继承之间的区别。经典继承仅限于从其他类继承的类。然而,原型继承不仅包括从其他原型继承的原型,还包括从原型继承的对象。
原型类同构
您一定已经注意到原型和类非常相似。确实如此。他们是。事实上,它们非常相似,您实际上可以使用原型对类进行建模:
function CLASS(base, body)
if (arguments.length < 2) body = base, base = Object.prototype;
var prototype = Object.create(base, new: value: create);
return body.call(prototype, base), prototype;
function create()
var self = Object.create(prototype);
return prototype.hasOwnProperty("constructor") &&
prototype.constructor.apply(self, arguments), self;
使用上面的CLASS
函数,您可以创建看起来像类的原型:
var Human = CLASS(function ()
var milliseconds = 1
, seconds = 1000 * milliseconds
, minutes = 60 * seconds
, hours = 60 * minutes
, days = 24 * hours
, years = 365.2425 * days;
this.constructor = function (name, sex, dob)
this.name = name;
this.sex = sex;
this.dob = dob;
;
this.age = function ()
return Math.floor((new Date - this.dob) / years);
;
);
var Man = CLASS(Human, function (Human)
this.constructor = function (name, dob)
Human.constructor.call(this, name, "male", dob);
if (this.age() < 18) throw new Error(name + " is a boy, not a man!");
;
);
var johnDoe = Man.new("John Doe", new Date(1970, 0, 1));
然而,反之则不成立(即您不能使用类来建模原型)。这是因为原型是对象,但类不是对象。它们是完全不同的抽象类型。
结论
总而言之,我们了解到抽象是“通过从特定示例中提取共同特征形成的一般概念”,而概括是“更具体抽象的抽象”时间>。我们还了解了原型继承和经典继承之间的区别,以及它们如何是同一枚硬币的两个面。
在临别之际,我想指出有两种原型继承模式:原型模式和构造函数模式。原型模式是原型继承的规范模式,而构造函数模式用于使原型继承看起来更像经典继承。我个人更喜欢原型模式。
【讨论】:
感谢您的精彩回答。我需要了解原型模式与构造函数模式相比如何更好。任何示例? 我在博客中写了一篇关于构造函数与原型的比较评论:aaditmshah.github.io/why-prototypal-inheritance-matters/… 那么,说我们在javascript中使用函数实现继承时,有点使用经典的继承模型,而使用普通对象的时候是原型继承(两者都在内部遵循原型继承),这样说对吗? ? @Swanidhi 不。如果您使用的是 JavaScript,那么您使用的是原型继承模型。然而,JavaScript 有两种原型继承方式:使用函数(即构造函数模式)和使用对象(即原型模式)。 @Swanidhi 不,它仍然是原型。 JavaScript 没有“类”,因此在经典的 JavaScript 中绝对没有,包括构造函数。它仍然是原型继承。只是一种奇怪的原型继承形式,人们将其与经典继承相混淆。简而言之,programming with classes = classical inheritance
、programming with prototypes = prototypal inheritance
、programming with constructors = weird form of prototypal inheritance that looks a lot like classical inheritance
。希望能澄清事情。【参考方案2】:
在进入继承之前,我们先来看看两个 primary 模型在 javascript 中创建实例(对象):
经典模型:对象是从蓝图(类)创建的
class Person
fn() ...
// or constructor function say, function Person()
// create instance
let person = new Person();
原型模型:对象是直接从另一个对象创建的。
// base object
let Person = fn()...
// instance
let person = Object.create(Person);
在任何一种情况下,继承*都是通过使用原型对象链接对象来实现的。
(*基类方法可以通过原型对象通过派生类访问,并且不需要显式存在于派生类中。)
这是一个很好的解释,可以更好地理解 (http://www.objectplayground.com/)
【讨论】:
【参考方案3】:狗是动物。苏珊娜是条狗。在经典继承中,Animal
是一个类,Dog
是Animal
的子类,suzanna
是Dog
的一个实例。
在原型继承中,没有类。你有一个animal
,它是一个对象。 dog
是另一个对象,它克隆和扩展 animal
(原型对象)。 suzanna
是第三个对象,它复制并扩展了dog
。
let animal = hasChlorophyl: false;
let dog = Object.create(animal);
Object.assign(dog,
speak()
console.log("Woof!");
);
let suzanna = Object.create(dog);
Object.assign(suzanna,
name: "Suzanna"
);
suzanna.speak();
如果你写Dog
而不是dog
,特别是如果你把Dog
写成某种“构造函数”,那么你不是在做原型继承;你正在做(伪)经典继承。您使用Object.create()
来实现这一点并不意味着您正在进行原型继承。
事实上,JavaScript 只支持原型继承。令人困惑的new
运算符和.prototype
属性的存在是为了使原型继承看起来像(伪)经典继承。
Douglas Crockford 在他的书“JavaScript: The Good Parts”中详细探讨了这一点。
【讨论】:
以上是关于javascript中的经典继承与原型继承的主要内容,如果未能解决你的问题,请参考以下文章