web-dev-qa-db-fra.com

héritage basé sur un prototype ou sur une classe

En JavaScript, chaque objet est à la fois une instance et une classe. Pour effectuer un héritage, vous pouvez utiliser n'importe quelle instance d'objet en tant que prototype.

En Python, C++, etc., il existe des classes et des instances en tant que concepts distincts. Pour effectuer un héritage, vous devez utiliser la classe de base pour créer une nouvelle classe, qui peut ensuite être utilisée pour produire des instances dérivées.

Pourquoi JavaScript va-t-il dans cette direction (orientation objet basée sur un prototype)? Quels sont les avantages (et les inconvénients) du prototype OO par rapport au traditionnel OO basé sur la classe)?

192
Stefano Borini

Il y a une centaine de problèmes de terminologie ici, principalement liés à une personne (pas à vous) qui essaie de faire sonner son idée comme étant la meilleure.

Tous les langages orientés objet doivent pouvoir traiter plusieurs concepts:

  1. encapsulation de données avec des opérations associées sur les données, appelées divers membres de données et fonctions de membre, ou encore données et méthodes, entre autres.
  2. l'héritage, la possibilité de dire que ces objets sont comme ces autres objets SAUF pour ces changements
  3. polymorphisme ("plusieurs formes") dans lequel un objet décide lui-même des méthodes à exécuter, de sorte que vous puissiez compter sur le langage pour acheminer correctement vos requêtes.

Maintenant, en ce qui concerne la comparaison:

La première chose est toute la question "classe" vs "prototype". L'idée a commencé à l'origine avec Simula, où chaque classe représentait, avec une méthode basée sur les classes, un ensemble d'objets partageant le même espace d'état (lire "valeurs possibles") et les mêmes opérations, formant ainsi une classe d'équivalence. Si vous regardez Smalltalk, vous pouvez ouvrir une classe et ajouter des méthodes, c’est la même chose que ce que vous pouvez faire en Javascript.

Later OO langues voulaient pouvoir utiliser la vérification de type statique, nous avons donc eu l'idée d'un ensemble de classes fixe au moment de la compilation. Dans la version à classe ouverte, vous disposiez de plus de flexibilité; version plus récente, vous avez eu la possibilité de vérifier certaines corrections du compilateur qui, autrement, auraient nécessité des tests.

Dans un langage "basé sur les classes", cette copie est effectuée au moment de la compilation. Dans un langage prototype, les opérations sont stockées dans la structure de données prototype, qui est copiée et modifiée au moment de l'exécution. En résumé, cependant, une classe est toujours la classe d'équivalence de tous les objets partageant le même espace d'état et les mêmes méthodes. Lorsque vous ajoutez une méthode au prototype, vous créez effectivement un élément d'une nouvelle classe d'équivalence.

Maintenant, pourquoi faire ça? principalement parce qu’il s’agit d’un mécanisme simple, logique et élégant au moment de l’exécution. maintenant, pour créer un nouvel objet, o pour créer une nouvelle classe, il vous suffit d’effectuer une copie en profondeur, en copiant toutes les données et la structure de données prototype. Vous obtenez l'héritage et le polymorphisme plus ou moins gratuitement alors: la recherche de méthode toujours consiste à demander à un dictionnaire l'implémentation d'une méthode par son nom.

La raison qui a abouti au script Javascript/ECMA est essentiellement que lorsque nous avons commencé à utiliser cette technologie il y a 10 ans, nous avions affaire à des ordinateurs beaucoup moins puissants et à des navigateurs beaucoup moins sophistiqués. Le choix de la méthode basée sur un prototype signifiait que l'interpréteur pourrait être très simple tout en préservant les propriétés souhaitables de l'orientation de l'objet.

190
Charlie Martin

On trouvera dans l’article - Self: le pouvoir de la simplicité . Le papier avance les arguments suivants en faveur des prototypes:

Création en copiant . La création de nouveaux objets à partir de prototypes s’effectue par une simple opération: copie, avec une simple métaphore biologique, le clonage. La création de nouveaux objets à partir de classes est réalisée par instanciation, ce qui inclut l'interprétation des informations de format d'une classe. L'instanciation est similaire à la construction d'une maison à partir d'un plan. La copie nous interpelle comme une métaphore plus simple que l’instanciation.

Exemples de modules préexistants . Les prototypes sont plus concrets que les classes car ce sont des exemples d'objets plutôt que des descriptions de format et d'initialisation. Ces exemples peuvent aider les utilisateurs à réutiliser des modules en les rendant plus faciles à comprendre. Un système basé sur un prototype permet à l'utilisateur d'examiner un représentant typique plutôt que de lui demander de donner un sens à sa description.

Support des objets uniques . Self fournit un cadre qui peut facilement inclure des objets uniques avec leur propre comportement. Étant donné que chaque objet a des emplacements nommés et que les emplacements peuvent contenir un état ou un comportement, tout objet peut avoir des emplacements ou un comportement uniques. Les systèmes basés sur des classes sont conçus pour des situations dans lesquelles de nombreux objets ont le même comportement. Il n’existe aucun support linguistique permettant à un objet de posséder son propre comportement et il est gênant ( think Singleton pattern) de créer une classe dont il est garanti qu’elle ne possède qu’une seule instance. Self ne souffre d'aucun de ces inconvénients. Tout objet peut être personnalisé avec son propre comportement. Un objet unique peut contenir le comportement unique et une "instance" distincte n'est pas nécessaire.

Élimination de méta-régression . Aucun objet dans un système basé sur une classe ne peut être autonome; un autre objet (sa classe) est nécessaire pour exprimer sa structure et son comportement. Cela conduit à une méta régression infiniment conceptuelle: un point est une instance de la classe Point, qui est une instance de la métaclasse Point, qui est une instance de la métamétaclasse Point, à l'infini. D'autre part, dans les systèmes basés sur des prototypes, un objet peut inclure son propre comportement; aucun autre objet n'est nécessaire pour lui donner vie. Les prototypes éliminent les méta-régressions.

Self est probablement la première langue à implémenter des prototypes. (Il a également été le pionnier d’autres technologies intéressantes, telles que JIT, qui a ensuite été transféré vers la JVM. La lecture de les autres Self Papers devrait également être instructive).

38
Vijay Mathew

Vous devriez vérifier un excellent livre sur JavaScript par Douglas Crockford . Il fournit une très bonne explication de certaines des décisions de conception prises par les créateurs JavaScript.

L'un des aspects importants de la conception de JavaScript est son système d'héritage prototypique. Les objets sont des citoyens de première classe en JavaScript, à tel point que les fonctions standard sont également implémentées en tant qu'objets (l'objet 'Function' pour être précis). À mon avis, lorsqu'il a été conçu à l'origine pour être utilisé dans un navigateur, il était destiné à être utilisé pour créer de nombreux objets singleton. Dans le DOM du navigateur, vous trouvez cette fenêtre, ce document, etc. tous les objets singleton. En outre, JavaScript est un langage dynamique faiblement typé (à la différence de Python qui est fortement typé, langage dynamique)). En conséquence, un concept d'extension d'objet a été implémenté grâce à l'utilisation de 'prototype' propriété.

Donc, je pense qu’il ya des avantages pour les prototypes basés sur OO tels qu’implémentés en JavaScript:

  1. Convient aux environnements faiblement typés, pas besoin de définir des types explicites.
  2. Il est incroyablement facile d’implémenter un motif singleton (comparez JavaScript et Java à cet égard, et vous saurez de quoi je parle).
  3. Fournit des moyens d'appliquer une méthode d'un objet dans le contexte d'un objet différent, d'ajouter et de remplacer des méthodes dynamiquement à partir d'un objet, etc. (choses qui ne sont pas possibles dans un langage fortement typé).

Voici quelques inconvénients du prototypal OO:

  1. Pas de moyen facile d'implémenter des variables privées. Il est possible d'implémenter des vars privés en utilisant la magie de Crockford en utilisant fermetures , mais ce n'est certainement pas aussi trivial que d'utiliser des variables privées dans say Java ou C #.
  2. Je ne sais pas encore comment implémenter plusieurs héritages (pour ce que cela vaut) dans JavaScript.
23
Amit