我搜索了这么多链接,并且不能很好地了解经典继承和原型继承之间的区别?
我从这些方面学到了一些东西,但我仍然对这些概念感到困惑。
经典继承
// 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);
这类似于经典继承吗?我对什么是原型继承感到困惑?什么是经典继承?为什么经典继承不好?
你能给我一个简单的例子,以便以一种简单的方式更好地理解这些。
谢谢,
希瓦
答案 0 :(得分:224)
您在问题中演示的代码示例都使用了原型继承。实际上,您使用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. |
+----------------------+----------------+---------------------------------------+
正如您在原型面向对象编程语言中所看到的,对象是现实世界实体(在这种情况下它们简称为对象)或其他对象的抽象(在这种情况下,它们被称为抽象的那些对象的原型) 。因此,原型是一种概括。
原型面向对象编程语言中的对象可以创建为ex-nihilo(即无中生有)或来自另一个对象(它成为新创建的对象的原型):
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));
然而事实并非如此(即你不能使用类来模拟原型)。这是因为原型是对象,但类不是对象。它们是完全不同的抽象类型。
总之,我们了解到抽象是&#34;通过从特定示例中提取共同特征而形成的一般概念&#34; 并且概括是&#34;抽象一个更具体的抽象&#34; 。我们还了解了原型和经典继承之间的差异,以及它们是如何同一枚硬币的两个面。
在分页上,我想说有两种原型继承模式:原型模式和构造模式。原型模式是原型继承的规范模式,而构造模式用于使原型继承看起来更像经典继承。我个人更喜欢原型模式。
P.S。我是撰写博客文章的人#34; Why Prototypal Inheritance Matters&#34;并回答了问题&#34; Benefits of prototypal inheritance over classical?&#34;。我的答案是接受的答案。
答案 1 :(得分:5)
Before jumping into inheritance, we will take a look at two primary models to create instances (objects) in javascript:
Classical model: Object is created from a blueprint (class)
class Person {
fn() {...}
} // or constructor function say, function Person() {}
// create instance
let person = new Person();
Prototypal model: Object is created directly from another object.
// base object
let Person = { fn(){...} }
// instance
let person = Object.create(Person);
In either case, Inheritance* is achieved by linking objects using prototype object.
(*base class methods are accessible via. derived class through the prototype object and not required to be explicitly present in derived class.)
Here is a good explanation to understand better (http://www.objectplayground.com/)
答案 2 :(得分:1)
狗是动物。苏珊娜是狗。在经典继承中,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:好的零件》一书中对此进行了详尽的探讨。