经典继承与javascript中的原型继承

Siv*_*ini 109 javascript inheritance

我搜索了这么多链接,并且不能很好地了解经典继承和原型继承之间的区别?

我从中学到了一些东西,但我仍然对这些概念感到困惑.

经典继承

// 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);
Run Code Online (Sandbox Code Playgroud)

经典继承是否在内部使用原型继承?

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);
Run Code Online (Sandbox Code Playgroud)

这类似于经典继承吗?我对什么是原型继承感到困惑?什么是经典继承?为什么经典继承不好?

你能给我一个简单的例子,以便以一种简单的方式更好地理解这些.

谢谢,

湿婆

Aad*_*hah 236

您在问题中演示的代码示例都使用了原型继承.实际上,您使用JavaScript编写的任何面向对象的代码都是原型继承的范例.JavaScript根本没有经典继承.这应该清楚一点:

                                   Inheritance
                                        |
                         +-----------------------------+
                         |                             |
                         v                             v
                    Prototypal                     Classical
                         |
         +------------------------------+
         |                              |
         v                              v
Prototypal Pattern             Constructor Pattern
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,原型继承和经典继承是两种不同的继承范式.Self,Lua和JavaScript等语言支持原型继承.但是,大多数语言如C++,Java和C#都支持经典继承.


面向对象编程的快速概述

原型继承和经典继承都是面向对象的编程范式(即它们处理对象).对象只是抽象,它封装了现实世界实体的属性(即它们代表程序中的真实单词).这被称为抽象.

抽象:计算机程序中现实世界事物的表现.

从理论上讲,抽象被定义为"通过从特定示例中提取共同特征而形成的一般概念".但是为了这个解释,我们将使用上述定义.

现在有些对象有很多共同点.例如,泥巴自行车和哈雷戴维森有很多共同之处.

泥地自行车:

一辆泥巴自行车.

哈利戴维森:

哈利戴维森

泥巴自行车和哈雷戴维森都是自行车.因此,自行车是泥浆自行车和哈雷戴维森的概括.

                   Bike
                     |
    +---------------------------------+
    |                                 |
    v                                 v
Mud Bike                       Harley Davidson
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,自行车,泥地自行车和哈雷戴维森都是抽象的.然而,自行车是泥地自行车和哈雷戴维森的更普遍的抽象(即泥地自行车和哈雷戴维森都是特定类型的自行车).

泛化:更具体抽象的抽象.

在面向对象的编程中,我们创建对象(它是真实世界实体的抽象),我们使用类或原型来创建这些对象的泛化.通过继承创建泛化.自行车是泥骑自行车的概括.因此泥巴自行车继承自行车.


经典的面向对象编程

在经典的面向对象编程中,我们有两种类型的抽象:类和对象.如前所述,对象是现实世界实体的抽象.另一方面,类是对象或另一个类的抽象(即它是一个泛化).例如,考虑:

+----------------------+----------------+---------------------------------------+
| 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.              |
+----------------------+----------------+---------------------------------------+
Run Code Online (Sandbox Code Playgroud)

正如您在经典的面向对象编程语言中所看到的,对象只是抽象(即所有对象的抽象级别为1),而类只是泛化(即所有类的抽象级别都大于1).

经典的面向对象编程语言中的对象只能通过实例化类来创建:

class Human {
    // ...
}

class Man extends Human {
    // ...
}

Man johnDoe = new Man();
Run Code Online (Sandbox Code Playgroud)

在经典的面向对象编程语言的总结中,对象是现实世界实体的抽象,而类是概括(即对象或其他类的抽象).

因此,随着抽象级别的增加,实体变得更加通用,并且随着抽象级别的降低,实体变得更加具体.在这个意义上,抽象级别类似于从更具体的实体到更一般的实体的范围.


原型面向对象编程

原型面向对象编程语言比传统的面向对象编程语言简单得多,因为在原型面向对象编程中我们只有一种类型的抽象(即对象).例如,考虑:

+----------------------+----------------+---------------------------------------+
| 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.              |
+----------------------+----------------+---------------------------------------+
Run Code Online (Sandbox Code Playgroud)

正如您在原型面向对象编程语言中所看到的,对象是现实世界实体(在这种情况下它们简称为对象)或其他对象的抽象(在这种情况下,它们被称为抽象的那些对象的原型).因此原型是一种概括.

原型面向对象编程语言中的对象可以是ex-nihilo(即无中生有)或来自另一个对象(它成为新创建的对象的原型):

var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);
Run Code Online (Sandbox Code Playgroud)

在我看来,原型面向对象编程语言比传统的面向对象编程语言更强大,因为:

  1. 只有一种抽象类型.
  2. 概括只是对象.

到目前为止,你必须意识到经典继承和原型继承之间的区别.经典继承仅限于从其他类继承的类.然而,原型继承不仅包括从其他原型继承的原型,还包括从原型继承的对象.


原型类同构

您必须注意到原型和类非常相似.确实如此.他们是.实际上它们非常相似,您实际上可以使用原型来建模类:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用上面的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));
Run Code Online (Sandbox Code Playgroud)

但事实并非如此(即您不能使用类来对原型进行建模).这是因为原型是对象,但类不是对象.它们是完全不同的抽象类型.


结论

总之,我们了解到抽象是"通过从特定示例中提取共同特征而形成的一般概念",并且泛化是"更具体抽象的抽象".我们还了解了原型和经典继承之间的差异,以及它们如何是同一枚硬币的两个面.

在分开的说明中,我想指出原型继承有两种模式:原型模式和构造模式.原型模式是原型继承的规范模式,而构造模式用于使原型继承看起来更像经典继承.我个人更喜欢原型模式.

PS我是写博客文章" 为什么原型继承很重要 "并回答了" 原型继承优于经典原型? "的问题.我的回答是接受的答案.

  • @Swanidhi不,它仍然是原型.JavaScript没有"类",因此在经典的JavaScript中绝对没有,包括构造函数.它仍然是原型继承.只是一种奇怪的原型继承形式,人们会混淆古典继承.简而言之,"使用类编程=经典继承","使用原型进行编程=原型继承","使用构造函数编程=原型继承的奇怪形式,看起来很像经典继承".希望澄清事情. (5认同)
  • 谢谢你的精彩回答.我需要了解原型模式如何比构造函数模式更好.一个例子? (2认同)

Ant*_*des 10

狗是一种动物。苏珊娜是一只狗。在经典继承中,Animal是一个类,Dog是 的子类Animal,并且suzanna是 的实例Dog

在原型继承中,没有类。你有一个animal,它是一个对象。Adog是另一个对象,它克隆并扩展了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();
Run Code Online (Sandbox Code Playgroud)

如果您编写Dog而不是dog,特别是如果您创建Dog某种“构造函数”函数,那么您就不是在进行原型继承;而是在进行原型继承。你正在做(伪)经典继承。您用于实现此目的的事实Object.create()并不意味着您正在执行原型继承。

事实上,JavaScript 仅支持原型继承。令人困惑的new运算符和.prototype属性的存在是为了使原型继承看起来像(伪)经典继承。

Douglas Crockford 在他的书《JavaScript:The Good Parts》中详细探讨了这一点。


eve*_*sto 7

在进入继承之前,我们将看一下在javascript中创建实例(对象)的两个主要模型:

经典模型:对象是根据蓝图创建的(类)

class Person {
  fn() {...}
} // or constructor function say, function Person() {}

// create instance
let person = new Person();
Run Code Online (Sandbox Code Playgroud)

原型模型:直接从另一个对象创建对象。

// base object
let Person = { fn(){...} }

// instance
let person = Object.create(Person);
Run Code Online (Sandbox Code Playgroud)

无论哪种情况,继承*都是通过使用原型对象链接对象来实现的。

(*基类方法可通过原型对象通过派生类访问,并且不需要在派生类中显式存在。)

这是一个很好的解释,可以帮助您更好地理解(http://www.objectplayground.com/