héritage classique vs héritage prototypique en javascript

118

J'ai cherché autant de liens sur Google et je ne peux pas me faire une bonne idée de la différence entre l'héritage classique et l'héritage prototypique?

J'ai appris certaines choses de ces derniers mais je suis toujours confus au sujet des concepts.

L'héritage classique

// 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);

L'héritage classique utilise-t-il l'héritage prototypique à l'intérieur?

http://aaditmshah.github.io/why-prototypal-inheritance-matters/

À partir du lien ci-dessus, j'ai appris que nous ne pouvons pas ajouter de nouvelles méthodes au moment de l'exécution dans l'héritage classique . Est-ce correct? Mais vous pouvez vérifier le code ci-dessus, je peux ajouter la méthode "move" et toutes les méthodes au moment de l'exécution via prototype . C'est donc un héritage classique basé sur un prototype? Si oui, qu'est-ce que l'héritage classique et l'héritage prototype? Je suis confus à ce sujet.

Héritage prototypique.

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);

Est-ce similaire à l'héritage classique? Je suis totalement confus sur ce qu'est l'héritage prototypique? Qu'est-ce que l'héritage classique? Pourquoi l'héritage classique est-il mauvais?

Pouvez-vous me donner un exemple simple pour mieux les comprendre de manière simple.

Merci,

Siva

SivaRajini
la source
Dupliquer, vérifiez ceci: stackoverflow.com/questions/1595611/…
Silviu Burcea
5
Je ne sais pas de quoi vous parlez ici - le premier bloc de code est l' héritage prototypique, pas classique. Votre deuxième bloc de code n'a aucun héritage!
Alnitak
Cela peut xplain: blog.stephenwyattbush.com/2012/05/01/…
HasanAboShally
@alnitak developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... ce lien indique que l'un était l'héritage classique. c'est pourquoi je suis confus.
SivaRajini
Pour en savoir plus sur les raisons pour lesquelles vous voudrez peut-être éviter l'héritage classique, consultez mon exposé, "L'héritage classique est obsolète: comment penser en prototypage OO" vimeo.com/69255635
Eric Elliott

Réponses:

248

Les deux exemples de code que vous avez présentés dans votre question utilisent l'héritage prototypique. En fait, tout code orienté objet que vous écrivez en JavaScript est un paradigme d'héritage prototypique. JavaScript n'a tout simplement pas d'héritage classique. Cela devrait clarifier un peu les choses:

                                   Inheritance
                                        |
                         +-----------------------------+
                         |                             |
                         v                             v
                    Prototypal                     Classical
                         |
         +------------------------------+
         |                              |
         v                              v
Prototypal Pattern             Constructor Pattern

Comme vous pouvez le voir, l'héritage prototypique et l'héritage classique sont deux paradigmes différents d'héritage. Certains langages comme Self, Lua et JavaScript prennent en charge l'héritage prototypique. Cependant, la plupart des langages comme C ++, Java et C # prennent en charge l'héritage classique.


Un aperçu rapide de la programmation orientée objet

L'héritage prototypique et l'héritage classique sont des paradigmes de programmation orientés objet (c'est-à-dire qu'ils traitent d'objets). Les objets sont simplement des abstractions qui encapsulent les propriétés d'une entité du monde réel (c'est-à-dire qu'ils représentent des mots réels dans le programme). C'est ce qu'on appelle l'abstraction.

Abstraction: représentation d'objets du monde réel dans des programmes informatiques.

Théoriquement, une abstraction est définie comme "un concept général formé en extrayant des caractéristiques communes à partir d'exemples spécifiques". Cependant, pour cette explication, nous allons utiliser la définition ci-dessus à la place.

Maintenant, certains objets ont beaucoup de choses en commun. Par exemple, un vélo de boue et une Harley Davidson ont beaucoup en commun.

Un vélo de boue:

Un vélo de boue.

Une Harley Davidson:

Une Harley Davidson

Un vélo de boue et une Harley Davidson sont tous deux des vélos. Par conséquent, un vélo est une généralisation à la fois d'un vélo de boue et d'une Harley Davidson.

                   Bike
                     |
    +---------------------------------+
    |                                 |
    v                                 v
Mud Bike                       Harley Davidson

Dans l'exemple ci-dessus, la moto, la moto de boue et la Harley Davidson sont toutes des abstractions. Cependant, le vélo est une abstraction plus générale du vélo de boue et de la Harley Davidson (c'est-à-dire que le vélo de boue et la Harley Davidson sont des types de vélos spécifiques).

Généralisation: une abstraction d'une abstraction plus spécifique.

Dans la programmation orientée objet, nous créons des objets (qui sont des abstractions d'entités du monde réel) et nous utilisons des classes ou des prototypes pour créer des généralisations de ces objets. Les généralisations sont créées par héritage. Un vélo est une généralisation d'un vélo de boue. C'est pourquoi les vélos de boue héritent des vélos.


Programmation classique orientée objet

Dans la programmation classique orientée objet, nous avons deux types d'abstractions: les classes et les objets. Un objet, comme mentionné précédemment, est une abstraction d'une entité du monde réel. Une classe par contre est une abstraction d'un objet ou d'une autre classe (c'est-à-dire une généralisation). Par exemple, considérez:

+----------------------+----------------+---------------------------------------+
| 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.              |
+----------------------+----------------+---------------------------------------+

Comme vous pouvez le voir dans les langages de programmation orientés objet classiques, les objets ne sont que des abstractions (c'est-à-dire que tous les objets ont un niveau d'abstraction de 1) et les classes ne sont que des généralisations (c'est-à-dire que toutes les classes ont un niveau d'abstraction supérieur à 1).

Les objets dans les langages de programmation orientés objet classiques ne peuvent être créés qu'en instanciant des classes:

class Human {
    // ...
}

class Man extends Human {
    // ...
}

Man johnDoe = new Man();

En résumé, dans les langages de programmation orientés objet classiques, les objets sont des abstractions d'entités du monde réel et les classes sont des généralisations (c'est-à-dire des abstractions d'objets ou d'autres classes).

Par conséquent, à mesure que le niveau d'abstraction augmente, les entités deviennent plus générales et à mesure que le niveau d'abstraction diminue, les entités deviennent plus spécifiques. En ce sens, le niveau d'abstraction est analogue à une échelle allant d'entités plus spécifiques à des entités plus générales.


Programmation orientée objet prototypique

Les langages de programmation orientés objet prototypiques sont beaucoup plus simples que les langages de programmation orientés objet classiques car dans la programmation orientée objet prototypique, nous n'avons qu'un seul type d'abstraction (c'est-à-dire les objets). Par exemple, considérez:

+----------------------+----------------+---------------------------------------+
| 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.              |
+----------------------+----------------+---------------------------------------+

Comme vous pouvez le voir dans les langages de programmation orientés objet prototypiques, les objets sont des abstractions d'entités du monde réel (auquel cas ils sont simplement appelés objets) ou d'autres objets (dans ce cas, ils sont appelés prototypes des objets qu'ils abstraits). Un prototype est donc une généralisation.

Les objets dans les langages de programmation orientés objet prototypiques peuvent être créés ex-nihilo (c'est-à-dire à partir de rien) ou à partir d'un autre objet (qui devient le prototype de l'objet nouvellement créé):

var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);

À mon humble avis, les langages de programmation orientés objet prototypiques sont plus puissants que les langages de programmation orientés objet classiques car:

  1. Il n'y a qu'un seul type d'abstraction.
  2. Les généralisations ne sont que des objets.

Vous devez maintenant avoir réalisé la différence entre l'héritage classique et l'héritage prototypique. L'héritage classique est limité aux classes héritant d'autres classes. Cependant, l'héritage prototypique comprend non seulement les prototypes héritant d'autres prototypes, mais également les objets héritant des prototypes.


Isomorphisme de classe prototype

Vous devez avoir remarqué que les prototypes et les classes sont très similaires. C'est vrai. Elles sont. En fait, ils sont si similaires que vous pouvez réellement utiliser des prototypes pour modéliser des classes:

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;
    }
}

En utilisant la CLASSfonction ci-dessus , vous pouvez créer des prototypes qui ressemblent à des classes:

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));

L'inverse n'est cependant pas vrai (c'est-à-dire que vous ne pouvez pas utiliser de classes pour modéliser des prototypes). En effet, les prototypes sont des objets mais les classes ne sont pas des objets. Il s'agit d'un type d'abstraction entièrement différent.


Conclusion

En résumé, nous avons appris qu'une abstraction est un «concept général formé en extrayant des caractéristiques communes d'exemples spécifiques» et que la généralisation est «une abstraction d'une abstraction plus spécifique» . Nous avons également appris les différences entre l'héritage prototypique et classique et comment les deux sont les deux faces d'une même pièce.

En guise de conclusion, je voudrais faire remarquer qu'il existe deux modèles d'héritage prototypique: le modèle prototypique et le modèle constructeur. Le modèle prototypique est le modèle canonique de l'héritage prototypique tandis que le modèle du constructeur est utilisé pour que l'héritage prototypique ressemble davantage à l'héritage classique. Personnellement, je préfère le modèle prototypique.

PS Je suis le type qui a écrit le billet de blog " Pourquoi l'héritage prototypique est important " et a répondu à la question " Avantages de l'héritage prototypique par rapport au classique? ". Ma réponse est la réponse acceptée.

Aadit M Shah
la source
2
merci pour votre merveilleuse réponse. J'ai besoin de comprendre comment le modèle prototypique est meilleur comparé au modèle de constructeur.
SivaRajini
1
J'ai écrit une critique comparative sur les constructeurs et les prototypes dans mon blog: aaditmshah.github.io/why-prototypal-inheritance-matters/…
Aadit M Shah
Alors, serait-il correct de dire que lorsque nous utilisons des fonctions en javascript pour obtenir l'héritage, nous utilisons un peu le modèle d'héritage classique et lorsque nous utilisons des objets simples, l'héritage prototypique (tous deux en interne suivant l'héritage prototypique)?
Swanidhi
1
@Swanidhi Non. Si vous utilisez JavaScript, vous utilisez le modèle d'héritage prototypique. Cependant, JavaScript a deux types d'héritage prototypique: l'utilisation de fonctions (c'est-à-dire le modèle du constructeur) et l'utilisation d'objets (c'est-à-dire le modèle prototypique).
Aadit M Shah
5
@Swanidhi Non. C'est encore prototypique. JavaScript n'a pas de «classes» et donc absolument rien en JavaScript en classique, y compris les constructeurs. C'est toujours un héritage prototypique. Juste une forme étrange d'héritage prototypique que les gens confondent avec l'héritage classique. En bref, 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. J'espère que cela clarifie les choses.
Aadit M Shah
8

Avant de passer à l'héritage, nous allons examiner deux modèles principaux pour créer des instances (objets) en javascript:

Modèle classique: l' objet est créé à partir d'un plan (classe)

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

// create instance
let person = new Person();

Modèle prototypique: l' objet est créé directement à partir d'un autre objet.

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

// instance
let person = Object.create(Person);

Dans les deux cas, l'héritage * est obtenu en liant des objets à l'aide d'un objet prototype.

(* les méthodes de classe de base sont accessibles via.

Voici une bonne explication pour mieux comprendre ( http://www.objectplayground.com/ )

Everlasto
la source
0

Un chien est un animal. Suzanna est un chien. Dans l'héritage classique, Animalest une classe, Dogest une sous-classe de Animalet suzannaest une instance de a Dog.

Dans l'héritage prototypique, il n'y a pas de classe. Vous avez un animal, qui est un objet. A dogest un autre objet, qui clone et étend animal(l'objet prototype). suzannaest un troisième objet, qui copie et étend 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();

Si vous écrivez à la Dogplace de dog, surtout si vous créez Dogune sorte de fonction "constructeur", alors vous ne faites pas d'héritage prototypique; vous faites un héritage (pseudo-) classique . Le fait que vous utilisez Object.create()pour y parvenir ne signifie pas que vous faites un héritage prototypique.

En fait, JavaScript ne prend en charge que l'héritage prototypique. L' newopérateur et l' .prototypeattribut déroutants sont là pour que l'héritage prototypique ressemble à un héritage (pseudo-) classique.

Douglas Crockford explore cela en détail dans son livre, "JavaScript: The Good Parts".

Antonis Christofides
la source