Qu'est-ce que le polymorphisme en Javascript?


90

J'ai lu un article que j'ai pu trouver sur Internet sur le polymorphisme . Mais je pense que je ne pouvais pas tout à fait en saisir le sens et son importance. La plupart des articles ne disent pas pourquoi c'est important et comment je peux obtenir un comportement polymorphe en POO (bien sûr en JavaScript).

Je ne peux pas fournir d'exemple de code car je n'ai pas l'idée de l'implémenter, donc mes questions sont ci-dessous:

  1. Qu'Est-ce que c'est?
  2. Pourquoi en avons-nous besoin?
  3. Comment ça fonctionne?
  4. Comment puis-je obtenir ce comportement polymorphe en javascript?

J'ai cet exemple. Mais on comprend aisément quel sera le résultat de ce code. Cela ne donne aucune idée claire du polymorphisme lui-même.

function Person(age, weight) {
    this.age = age;
    this.weight = weight;
    this.getInfo = function() {
        return "I am " + this.age + " years old " +
        "and weighs " + this.weight +" kilo.";
    }
}
function Employee(age, weight, salary) {
    this.salary = salary;
    this.age = age;
    this.weight = weight;
    this.getInfo = function() {
        return "I am " + this.age + " years old " +
        "and weighs " + this.weight +" kilo " +
        "and earns " + this.salary + " dollar.";
    }
}

Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
  // The argument, 'obj', can be of any kind
  // which method, getInfo(), to be executed depend on the object
  // that 'obj' refer to.

function showInfo(obj) {
    document.write(obj.getInfo() + "<br>");
}

var person = new Person(50,90);
var employee = new Employee(43,80,50000);
showInfo(person);
showInfo(employee);

Cette question est probablement trop large pour fonctionner correctement avec StackOverflow. Le mieux que nous puissions faire serait de vous relier à une autre explication du polymorphisme. StackOverflow est le meilleur pour répondre à des questions spécifiques ou des clarifications sur un problème spécifique, comme "La source a dit que le polymorphisme était XYZ, mais que signifie Y?"
Vitruvius

2
vous n'en avez pas besoin. du tout. vous n'avez même pas besoin de classes dans JS, et en fait, il existe de nombreux autres paradigmes, sans doute meilleurs, pour la construction d'applications. apply / call / bind élimine le besoin d'homogénéité, et avec soft-object, vous pouvez modifier n'importe quoi pour répondre à vos besoins sans le pré-décorer ou hériter de cas particuliers.
dandavis

1
Le polymorphisme n'est pas seulement lié à OO, et il a de nombreuses significations. Vous voudrez peut-être lire cette autre réponse sous les questions Le polymorphisme est-il possible sans héritage .
Edwin Dalorzo

L'héritage est généralement mal fait en JavaScript. Créer une instance de Parent à utiliser comme prototype de Child montre un manque de compréhension du rôle que jouent la fonction constructeur et le prototype dans la définition et la création d'un objet. Plus d'informations sont disponibles dans cette réponse: stackoverflow.com/a/16063711/1641941
HMR

Réponses:


99

Le polymorphisme est l'un des principes de la programmation orientée objet (POO). C'est la pratique de concevoir des objets pour partager des comportements et pour pouvoir remplacer des comportements partagés par des comportements spécifiques. Le polymorphisme tire parti de l'héritage pour y parvenir.

En POO, tout est considéré comme modélisé comme un objet. Cette abstraction peut être portée jusqu'aux écrous et boulons pour une voiture, ou aussi large qu'un simple type de voiture avec une année, une marque et un modèle.

Pour avoir un scénario de voiture polymorphe, il y aurait le type de voiture de base, puis il y aurait des sous-classes qui hériteraient de la voiture et fourniraient leurs propres comportements en plus des comportements de base qu'une voiture aurait. Par exemple, une sous-classe pourrait être TowTruck qui aurait encore une année de marque et de modèle, mais pourrait également avoir des comportements et des propriétés supplémentaires qui pourraient être aussi basiques qu'un drapeau pour IsTowing aussi compliqué que les spécificités de l'ascenseur.

Pour revenir à l'exemple des personnes et des employés, tous les employés sont des personnes, mais tous les gens ne sont pas des employés. C'est-à-dire que les gens seront la super classe et employés la sous-classe. Les gens peuvent avoir des âges et des poids, mais ils n'ont pas de salaire. Les employés sont des personnes donc ils auront intrinsèquement un âge et un poids, mais aussi parce qu'ils sont des employés ils auront un salaire.

Donc, pour faciliter cela, nous allons d'abord écrire la super classe (Person)

function Person(age,weight){
 this.age = age;
 this.weight = weight;
}

Et nous donnerons à Person la possibilité de partager ses informations

Person.prototype.getInfo = function(){
 return "I am " + this.age + " years old " +
    "and weighs " + this.weight +" kilo.";
};

Ensuite, nous souhaitons avoir une sous-classe de Personne, Employé

function Employee(age,weight,salary){
 this.age = age;
 this.weight = weight;
 this.salary = salary;
}
Employee.prototype = new Person();

Et nous remplacerons le comportement de getInfo en définissant celui qui convient le mieux à un employé

Employee.prototype.getInfo = function(){
 return "I am " + this.age + " years old " +
    "and weighs " + this.weight +" kilo " +
    "and earns " + this.salary + " dollar.";  
};

Ceux-ci peuvent être utilisés de la même manière que votre utilisation de code d'origine

var person = new Person(50,90);
var employee = new Employee(43,80,50000);

console.log(person.getInfo());
console.log(employee.getInfo());

Cependant, il n'y a pas grand-chose à gagner en utilisant l'héritage ici car le constructeur de Employee est si similaire à celui de person, et la seule fonction du prototype est remplacée. Le pouvoir de la conception polymorphe est de partager les comportements.


2
@ user3138436 - C'est correct. La chaîne prototypique sera inspectée pour la première occurrence de getInfoce qui sera celle de l'employé car elle est plus élevée dans la chaîne que celle de la personne. C'est ce que je voulais dire quand j'ai dit «remplacé».
Travis J

17
Vous n'utilisez pas le constructeur (Person.call (this, arg)) et définissez le prototype Employee sur une instance de Person. Le prototype est l'endroit où vont les membres partagés et la fonction constructeur est l'endroit où les membres spécifiques à l'instance sont créés. Vos exemples utilisent du code copier-coller réutilisation de la fonction constructeur et héritent de la partie prototype de manière erronée (Person a des membres spécifiques à l'instance qui n'ont aucune activité sur Employee.prototype, surtout si vous avez des membres mutables). Plus d'informations sur l'héritage à l'aide des fonctions du constructeur et du prototype ici: stackoverflow.com/a/16063711/1641941
HMR

3
Pour que l'employé réutilise et étende getinfo de la personne, il peut simplement le faire au return Person.prototype.getInfo.call(this) + + "and earns " + this.salary + " dollar.";lieu de copier-coller le code.
HMR

3
ici où s'applique le polymorphisme?
albert Jegani

2
@rpeg le point de polymorphisme est mieux vu dans l'exemple des OP. la fonction showInfo (); accepte un objet général. le polymorphisme est désormais la capacité de réagir différemment selon le type d'objet. Je pense que cette réponse ne rend pas cela assez clair, car elle appelle getInfo () sur chaque objet spécifique.
Stefan

26

Comme expliqué dans cette autre réponse , le polymorphisme a des interprétations différentes.

La meilleure explication sur le sujet que j'ai jamais lue est un article de Luca Cardelli , un théoricien des types renommé. L'article est intitulé Présentation des types, abstraction des données et polymorphisme .

Qu'Est-ce que c'est?

Cardelli définit plusieurs types de polymorphisme dans cet article:

  • Universel
    • paramétrique
    • inclusion
  • Ad hoc
    • surcharge
    • coercition

Peut-être qu'en JavaScript, il est un peu plus difficile de voir les effets du polymorphisme parce que les types plus classiques de polymorphisme sont plus évidents dans les systèmes de types statiques, alors que JavaScript a un système de types dynamiques.

Ainsi, par exemple, il n'y a pas de surcharge de méthode ou de fonction ou de coercition de type automatique au moment de la compilation en JavaScript. Dans un langage dynamique, nous prenons la plupart de ces choses pour acquis. Nous n'avons pas non plus besoin de quelque chose comme le polymorphisme paramétrique en JavaScript en raison de la nature dynamique du langage.

Néanmoins, JavaScript a une forme d'héritage de type qui émule les mêmes idées de polymorphisme de sous-type (classé comme polymorphisme d'inclusion par Cardelli ci-dessus) d'une manière similaire à ce que nous faisons généralement dans d'autres langages de programmation orientés objet comme Java ou C # (comme expliqué dans une autre réponse que j'ai partagée ci-dessus).

Une autre forme de polymorphisme très typique des langages dynamiques s'appelle le typage canard .

C'est une erreur de croire que le polymorphisme n'est lié qu'à la programmation orientée objet. D'autres modèles de programmation (fonctionnels, procéduraux, logiques, etc.) offrent différentes formes de polymorphisme dans leurs systèmes de types, probablement d'une manière un peu peu familière à ceux utilisés uniquement pour la POO.

Pourquoi en avons-nous besoin?

Le polymorphisme favorise de nombreux bons attributs dans les logiciels, entre autres, il favorise la modularité et la réutilisabilité et rend le système de caractères plus flexible et malléable. Sans cela, il serait vraiment difficile de raisonner sur les types. Le polymorphisme garantit qu'un type peut être remplacé par d'autres compatibles à condition qu'ils satisfassent une interface publique, ce qui favorise également le masquage des informations et la modularité.

Comment ça marche?

Ce n'est pas simple de répondre, différentes langues ont différentes manières de l'implémenter. Dans le cas de JavaScript, comme mentionné ci-dessus, vous le verrez se matérialiser sous la forme de hiérarchies de types utilisant l' héritage prototypique et vous pouvez également l'exploiter en utilisant le typage canard.

Le sujet est un peu large et vous avez ouvert deux nombreuses questions dans un seul post. Il est peut-être préférable de commencer par lire l'article de Cardelli, puis d'essayer de comprendre le polymorphisme indépendamment de tout langage ou paradigme de programmation, puis vous commencerez à faire des associations entre les concepts théoriques et ce qu'un langage particulier comme JavaScript a à offrir pour mettre en œuvre ces idées.


14

Quel est le but du polymorphisme?

Le polymorphisme rend un système de type statique plus flexible sans perdre la sécurité de type statique (significative) en assouplissant les conditions d'équivalence de type. La preuve reste qu'un programme ne fonctionnera que s'il ne contient aucune erreur de type.

Une fonction polymorphe ou un type de données est plus général qu'un monomorphe, car il peut être utilisé dans un plus large éventail de scénarios. En ce sens, le polymorphisme représente l'idée de généralisation dans des langages strictement typés.

Comment cela s'applique-t-il à Javascript?

Javascript a un système de type faible et dynamique. Un tel système de type est équivalent à un système de type strict ne contenant qu'un seul type. Nous pouvons considérer un tel type comme un énorme type d'union (pseudo syntaxe):

type T =
 | Undefined
 | Null
 | Number
 | String
 | Boolean
 | Symbol
 | Object
 | Array
 | Map
 | ...

Chaque valeur sera associée à l'une de ces alternatives de type au moment de l'exécution. Et comme Javascript est faiblement typé, chaque valeur peut changer de type n'importe quel nombre de fois.

Si nous adoptons une perspective théorique de type et considérons qu'il n'y a qu'un seul type, nous pouvons dire avec certitude que le système de types de Javascript n'a pas de notion de polymorphisme. Au lieu de cela, nous avons le typage canard et la coercition de type implicite.

Mais cela ne devrait pas nous empêcher de penser aux types dans nos programmes. En raison du manque de types en Javascript, nous devons les déduire pendant le processus de codage. Notre esprit doit remplacer le compilateur manquant, c'est-à-dire que dès que nous regardons un programme, nous devons reconnaître non seulement les algorithmes, mais aussi les types sous-jacents (peut-être polymorphes). Ces types nous aideront à construire des programmes plus fiables et plus robustes.

Afin de faire cela correctement, je vais vous donner un aperçu des manifestations les plus courantes du polymorphisme.

Polymorphisme paramétrique (aka génériques)

Le polymorphisme paramétrique dit que différents types sont interchangeables parce que les types n'ont pas du tout d'importance. Une fonction qui définit un ou plusieurs paramètres de type polymorphe paramétrique ne doit rien savoir des arguments correspondants mais les traiter tout de même, car ils peuvent adopter n'importe quel type. C'est assez restrictif, car une telle fonction ne peut fonctionner qu'avec les propriétés de ses arguments qui ne font pas partie de leurs données:

// parametric polymorphic functions

const id = x => x;

id(1); // 1
id("foo"); // "foo"

const k = x => y => x;
const k_ = x => y => y;

k(1) ("foo"); // 1
k_(1) ("foo"); // "foo"

const append = x => xs => xs.concat([x]);

append(3) ([1, 2]); // [1, 2, 3]
append("c") (["a", "b"]); // ["a", "b", "c"]

Polymorphisme ad hoc (alias surcharge)

Le polymorphisme ad hoc dit que différents types sont équivalents dans un but précis uniquement. Pour être équivalent dans ce sens, un type doit implémenter un ensemble de fonctions spécifiques à cet objectif. Une fonction qui définit un ou plusieurs paramètres de type polymorphe ad hoc a alors besoin de savoir quels ensembles de fonctions sont associés à chacun de ses arguments.

Le polymorphisme ad hoc rend une fonction compatible avec un plus grand domaine de types. L'exemple suivant illustre l'objectif du «mappage» et la manière dont les types peuvent implémenter cette contrainte. Au lieu d'un ensemble de fonctions, la contrainte "mappable" ne comprend qu'une seule mapfonction:

// Option type
class Option {
  cata(pattern, option) {
    return pattern[option.constructor.name](option.x);
  }
  
  map(f, opt) {
    return this.cata({Some: x => new Some(f(x)), None: () => this}, opt);
  }
};

class Some extends Option {
  constructor(x) {
    super(x);
    this.x = x;
  }
};

class None extends Option {
  constructor() {
    super();
  }
};


// ad-hoc polymorphic function
const map = f => t => t.map(f, t);

// helper/data

const sqr = x => x * x;

const xs = [1, 2, 3];
const x = new Some(5);
const y = new None();

// application

console.log(
  map(sqr) (xs) // [1, 4, 9]
);

console.log(
  map(sqr) (x) // Some {x: 25}
);

console.log(
  map(sqr) (y) // None {}
);

Polymorphisme de sous-type

Puisque d'autres réponses couvrent déjà le polymorphisme de sous-type, je l'ignore.

Polymorphisme structurel (aka sous-typage strutrual)

Le polymorphisme structurel dit que différents types sont équivalents, s'ils contiennent la même structure de telle manière, qu'un type a toutes les propriétés de l'autre mais peut inclure des propriétés supplémentaires. Cela étant dit, le polymorphisme structurel est un type de canard au moment de la compilation et offre certainement une sécurité de type supplémentaire. Mais en prétendant que deux valeurs sont du même type simplement parce qu'elles partagent certaines propriétés, on ignore complètement le niveau sémantique des valeurs:

const weight = {value: 90, foo: true};
const speed =  {value: 90, foo: false, bar: [1, 2, 3]};

Malheureusement, speedest considéré comme un sous-type de weightet dès que nous comparons les valuepropriétés, nous comparons virtuellement des pommes avec des oranges.


1
Bien que ce soit à bien des égards plus précis (et certainement plus complet) que la réponse acceptée, il n'a pas la même accessibilité: cette réponse suppose que le demandeur est déjà trop intelligent pour se soucier de la question :)
Jared Smith

@JaredSmith J'ai essayé de résumer le sujet en quelques paragraphes faciles à comprendre. Mais plus je perce, plus cela devient complexe. Je n'ai jamais trouvé une bonne source pour le polymorphisme dans les langues non typées, donc je pense que cette réponse est néanmoins précieuse.

l'édition améliore grandement l'accessibilité. Quant à l'utilité du polymorphisme dans les langages dynamiques, il y en a beaucoup mais j'ai du mal à penser à un bon exemple en JS. Un meilleur exemple serait les méthodes magiques de Python qui permettent aux types définis par l'utilisateur de fonctionner avec des fonctions polymorphes telles que len. Ou peut-être conjde clojure.
Jared Smith

8

qu'Est-ce que c'est?

Poly = plusieurs, morphisme = changement de forme ou de comportement.

pourquoi en avons-nous besoin?

En programmation, il est utilisé lorsque nous voulons que l'interface d'une fonction (disons la fonction X) soit suffisamment flexible pour accepter différents types ou nombre de paramètres. De plus, en fonction de la modification des types de paramètres ou des nombres, nous pouvons souhaiter que la fonction X se comporte différemment (morphisme).

Comment ça fonctionne?

Nous écrivons plusieurs implémentations de la fonction X où chaque implémentation accepte différents types de paramètres ou nombre de paramètres. En fonction du type ou du nombre de paramètres, le compilateur (au moment de l'exécution) décide quelle implémentation de X doit être exécutée lorsque X est appelé à partir d'un code.

comment puis-je obtenir ce comportement polymorphe en javascript?

JS n'est pas un langage typé, donc il n'est pas vraiment destiné à utiliser des concepts de POO comme le polymorphisme. Cependant, la nouvelle version de JS inclut désormais des classes et il est possible que le polymosphisme commence également à avoir un sens dans JS. D'autres réponses fournissent des solutions de contournement intéressantes.


3

Le polymorphisme signifie la capacité d'appeler la même méthode sur différents objets et chaque objet répond de manière différente s'appelle POLYMORPHISME .

    function Animal(sound){
    this.sound=sound;
    this.speak=function(){
    			return this.sound;
    	}
    }
//one method 
    function showInfo(obj){
    		console.log(obj.speak());
    }
//different objects
    var dog = new Animal("woof");
    var cat = new Animal("meow");
    var cow = new Animal("humbow");
//responds different ways
    showInfo(dog);
    showInfo(cat);
    showInfo(cow);


2

JavaScript est un langage interprété, pas un langage compilé.

Polymorhisme à la compilation (ou polymorphisme statique) Le polymorphisme à la compilation n'est rien d'autre que la surcharge de méthodes en java, c ++

La surcharge de méthode n'est donc pas possible en javascript.

Mais le polymorphisme dynamique (à l'exécution) est le polymorphisme qui existait au moment de l'exécution, donc le remplacement de méthode est possible en javascript

un autre exemple est PHP.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.