J'en suis arrivé à un point où j'ai besoin d'une sorte d'héritage multiple rudimentaire en JavaScript. (Je ne suis pas ici pour discuter de la question de savoir si c'est une bonne idée ou non, alors veuillez garder ces commentaires pour vous.)
Je veux juste savoir si quelqu'un a tenté cela avec un succès (ou pas), et comment ils l'ont fait.
Pour résumer, ce dont j'ai vraiment besoin, c'est de pouvoir avoir un objet capable d'hériter d'une propriété de plus d'une chaîne de prototypes (c'est-à-dire que chaque prototype pourrait avoir sa propre chaîne), mais dans un ordre de préséance donné (il sera rechercher les chaînes dans l'ordre pour la première définition).
Pour démontrer comment cela est théoriquement possible, cela pourrait être réalisé en attachant la chaîne secondaire à l'extrémité de la chaîne primaire, mais cela affecterait toutes les instances de l'un de ces prototypes précédents et ce n'est pas ce que je veux.
Pensées?
Réponses:
L'héritage multiple peut être obtenu dans ECMAScript 6 à l'aide d' objets Proxy .
la mise en oeuvre
Explication
Un objet proxy se compose d'un objet cible et de quelques interruptions, qui définissent un comportement personnalisé pour les opérations fondamentales.
Lors de la création d'un objet qui hérite d'un autre, nous utilisons
Object.create(obj)
. Mais dans ce cas, nous voulons l'héritage multiple, donc au lieu d'obj
utiliser un proxy qui redirigera les opérations fondamentales vers l'objet approprié.J'utilise ces pièges:
has
piège est un piège pour l'in
opérateur . J'utilisesome
pour vérifier si au moins un prototype contient la propriété.get
piège est un piège pour obtenir des valeurs de propriété. J'utilisefind
pour trouver le premier prototype qui contient cette propriété, et je renvoie la valeur, ou j'appelle le getter sur le récepteur approprié. Ceci est géré parReflect.get
. Si aucun prototype ne contient la propriété, je retourneundefined
.set
piège est un piège pour définir les valeurs de propriété. J'utilisefind
pour trouver le premier prototype qui contient cette propriété, et j'appelle son setter sur le récepteur approprié. S'il n'y a pas de setter ou si aucun prototype ne contient la propriété, la valeur est définie sur le récepteur approprié. Ceci est géré parReflect.set
.enumerate
piège est un piège pour lesfor...in
boucles . J'itère les propriétés énumérables du premier prototype, puis du second, et ainsi de suite. Une fois qu'une propriété a été itérée, je la stocke dans une table de hachage pour éviter de l'itérer à nouveau.Avertissement : cette interruption a été supprimée dans le brouillon ES7 et est obsolète dans les navigateurs.
ownKeys
piège est un piège pourObject.getOwnPropertyNames()
. Depuis ES7, lesfor...in
boucles continuent d'appeler [[GetPrototypeOf]] et d'obtenir les propres propriétés de chacune. Donc, afin de faire itérer les propriétés de tous les prototypes, j'utilise ce piège pour faire apparaître toutes les propriétés héritées énumérables comme des propriétés propres.getOwnPropertyDescriptor
piège est un piège pourObject.getOwnPropertyDescriptor()
. Faire apparaître toutes les propriétés énumérables comme des propriétés propres dans leownKeys
trap ne suffit pas, lesfor...in
boucles obtiendront le descripteur pour vérifier si elles sont énumérables. J'utilise doncfind
pour trouver le premier prototype qui contient cette propriété, et j'itère sa chaîne prototypique jusqu'à ce que je trouve le propriétaire, et je renvoie son descripteur. Si aucun prototype ne contient la propriété, je retourneundefined
. Le descripteur est modifié pour le rendre configurable, sinon nous pourrions casser certains invariants de proxy.preventExtensions
etdefineProperty
ne sont incluses que pour empêcher ces opérations de modifier la cible proxy. Sinon, nous pourrions finir par casser certains invariants de proxy.Il y a plus de pièges disponibles, que je n'utilise pas
getPrototypeOf
piège pourrait être ajouté, mais il n'y a pas de moyen approprié de renvoyer les multiples prototypes. Cela impliqueinstanceof
ne fonctionnera pas non plus. Par conséquent, je le laisse obtenir le prototype de la cible, qui est initialement nul.setPrototypeOf
piège pourrait être ajouté et accepter un tableau d'objets, qui remplaceraient les prototypes. Ceci est laissé comme exercice pour le lecteur. Ici, je le laisse simplement modifier le prototype de la cible, ce qui n'est pas très utile car aucun trap n'utilise la cible.deleteProperty
piège est un piège pour supprimer ses propres propriétés. Le proxy représente l'héritage, donc cela n'aurait pas beaucoup de sens. Je le laisse tenter la suppression sur la cible, qui ne devrait avoir aucune propriété de toute façon.isExtensible
piège est un piège pour obtenir l'extensibilité. Pas très utile, étant donné qu'un invariant l'oblige à retourner la même extensibilité que la cible. Je le laisse donc simplement rediriger l'opération vers la cible, qui sera extensible.apply
etconstruct
sont des pièges pour appeler ou instancier. Ils ne sont utiles que lorsque la cible est une fonction ou un constructeur.Exemple
la source
multiInherit(o1={a:1}, o2={b:2}, o3={a:3, b:3})
Object.assign
) ou l'obtention d'un graphe assez différent, à la fin toutes obtiennent une chaîne de prototypes unique entre les objets. La solution proxy offre un branchement à l'exécution, et c'est génial!Mise à jour (2019): le message d'origine devient assez obsolète. Cet article (maintenant lien d'archive Internet, puisque le domaine a disparu) et sa bibliothèque GitHub associée sont une bonne approche moderne.
Message original: Héritage multiple [modifier, pas l'héritage approprié du type, mais des propriétés; mixins] en Javascript est assez simple si vous utilisez des prototypes construits plutôt que des prototypes génériques. Voici deux classes parentes dont hériter:
Notez que j'ai utilisé le même membre "nom" dans chaque cas, ce qui pourrait être un problème si les parents n'étaient pas d'accord sur la façon dont le "nom" devrait être traité. Mais ils sont compatibles (redondants, vraiment) dans ce cas.
Maintenant, nous avons juste besoin d'une classe qui hérite des deux. L'héritage se fait en appelant la fonction constructeur (sans utiliser le mot clé new) pour les prototypes et les constructeurs d'objets. Premièrement, le prototype doit hériter des prototypes parents
Et le constructeur doit hériter des constructeurs parents:
Vous pouvez maintenant cultiver, manger et récolter différentes instances:
la source
Array.call(...)
mais cela ne semble pas affecter ce que je passe pourthis
.Array.prototype.constructor.call()
Celui-ci sert
Object.create
à faire une véritable chaîne prototype:Par exemple:
retournera:
de sorte que
obj.a === 1
,obj.b === 3
etc.la source
J'aime l'implémentation par John Resig d'une structure de classe: http://ejohn.org/blog/simple-javascript-inheritance/
Cela peut être simplement étendu à quelque chose comme:
ce qui vous permettra de passer plusieurs objets dont hériter. Vous allez perdre la
instanceOf
capacité ici, mais c'est une évidence si vous voulez l'héritage multiple.mon exemple plutôt alambiqué de ce qui précède est disponible sur https://github.com/cwolves/Fetch/blob/master/support/plugins/klass/klass.js
Notez qu'il y a du code mort dans ce fichier, mais cela permet l'héritage multiple si vous voulez jeter un coup d'œil.
Si vous voulez l'héritage chaîné (PAS l'héritage multiple, mais pour la plupart des gens, c'est la même chose), cela peut être accompli avec Class comme:
ce qui préservera la chaîne de prototypes d'origine, mais vous aurez également beaucoup de code inutile en cours d'exécution.
la source
Ne vous méprenez pas avec les implémentations de framework JavaScript d'héritage multiple.
Tout ce que vous avez à faire est d'utiliser Object.create () pour créer un nouvel objet à chaque fois avec l'objet prototype et les propriétés spécifiés, puis assurez-vous de modifier Object.prototype.constructor à chaque étape si vous prévoyez d'instancier
B
dans le futur.Pour hériter des propriétés d'occurrence
thisA
etthisB
nous utilisons Function.prototype.call () à la fin de chaque fonction d'objet. Ceci est facultatif si vous ne vous souciez que d'hériter du prototype.Exécutez le code suivant quelque part et observez
objC
:B
hérite du prototype deA
C
hérite du prototype deB
objC
est une instance deC
Ceci est une bonne explication des étapes ci-dessus:
POO en JavaScript: ce que vous devez savoir
la source
Je ne suis en aucun cas un expert de la POO javascript, mais si je vous comprends bien, vous voulez quelque chose comme (pseudo-code):
Dans ce cas, j'essaierais quelque chose comme:
la source
c.prototype
plusieurs fois ne donne pas plusieurs prototypes. Par exemple, si vous l'aviez faitAnimal.isAlive = true
,Cat.isAlive
serait toujours indéfini.Il est possible d'implémenter l'héritage multiple en JavaScript, bien que très peu de bibliothèques le fassent.
Je pourrais pointer Ring.js , le seul exemple que je connaisse.
la source
J'y travaillais beaucoup aujourd'hui et j'essayais d'y parvenir moi-même dans ES6. La façon dont je l'ai fait a été d'utiliser Browserify, Babel et ensuite je l'ai testé avec Wallaby et cela a semblé fonctionner. Mon objectif est d'étendre la baie actuelle, d'inclure ES6, ES7 et d'ajouter des fonctionnalités personnalisées supplémentaires dont j'ai besoin dans le prototype pour traiter les données audio.
Wallaby passe 4 de mes tests. Le fichier example.js peut être collé dans la console et vous pouvez voir que la propriété 'includes' est dans le prototype de la classe. Je veux toujours tester cela plus demain.
Voici ma méthode: (Je vais probablement refactoriser et reconditionner en module après un peu de sommeil!)
Github Repo: https://github.com/danieldram/array-includes-polyfill
la source
Je pense que c'est ridiculement simple. Le problème ici est que la classe enfant ne fera référence qu'à
instanceof
la première classe que vous appelezhttps://jsfiddle.net/1033xzyt/19/
la source
Vérifiez le code ci-dessous qui EST montrant la prise en charge de l'héritage multiple. Fait en utilisant PROTOTYPAL INHERITANCE
la source
J'ai tout à fait la fonction de permettre aux classes d'être définies avec l'héritage multiple. Il permet un code comme celui-ci. Dans l'ensemble, vous noterez un départ complet des techniques de classement natives en javascript (par exemple, vous ne verrez jamais le
class
mot - clé):pour produire une sortie comme celle-ci:
Voici à quoi ressemblent les définitions de classe:
Nous pouvons voir que chaque définition de classe utilisant la
makeClass
fonction accepte unObject
des noms de classe parente mappés à des classes parentes. Il accepte également une fonction qui retourneObject
des propriétés contenant pour la classe en cours de définition. Cette fonction a un paramètreprotos
, qui contient suffisamment d'informations pour accéder à toute propriété définie par l'une des classes parentes.Le dernier élément requis est la
makeClass
fonction elle-même, qui fait pas mal de travail. Le voici, avec le reste du code. J'aimakeClass
beaucoup commenté :La
makeClass
fonction prend également en charge les propriétés de classe; ceux-ci sont définis en préfixant les noms de propriété avec le$
symbole (notez que le nom de propriété final qui en résulte sera$
supprimé). Dans cet esprit, nous pourrions écrire uneDragon
classe spécialisée qui modélise le "type" du Dragon, où la liste des types de Dragon disponibles est stockée sur la classe elle-même, par opposition aux instances:Les défis de l'héritage multiple
Quiconque a suivi le code de
makeClass
près notera un phénomène indésirable assez important se produisant silencieusement lorsque le code ci-dessus s'exécute: l' instanciation de aRunningFlying
entraînera DEUX appels auNamed
constructeur!C'est parce que le graphique d'héritage ressemble à ceci:
Lorsqu'il y a plusieurs chemins vers la même classe parente dans le graphe d'héritage d'une sous-classe , les instanciations de la sous-classe invoqueront le constructeur de cette classe parente plusieurs fois.
Combattre cela n'est pas trivial. Regardons quelques exemples avec des noms de classes simplifiés. Nous allons considérer la classe
A
, la classe parente la plus abstraite, les classesB
etC
, qui héritent toutes deux deA
, et la classeBC
qui hérite deB
etC
(et donc conceptuellement "hérite en double" deA
):Si nous voulons éviter
BC
de double-invoquer,A.prototype.init
nous devrons peut-être abandonner le style d'appel direct des constructeurs hérités. Nous aurons besoin d'un certain niveau d'indirection pour vérifier si des appels en double se produisent, et court-circuiter avant qu'ils ne se produisent.Nous pourrions envisager de changer les paramètres fournis à la fonction properties: à côté
protos
, uneObject
contenant des données brutes décrivant les propriétés héritées, nous pourrions également inclure une fonction utilitaire pour appeler une méthode d'instance de telle sorte que les méthodes parentes soient également appelées, mais que les appels en double soient détectés et empêché. Jetons un œil à l'endroit où nous établissons les paramètres pourpropertiesFn
Function
:Le but de la modification ci-dessus
makeClass
est de faire en sorte que nous ayons un argument supplémentaire fourni à notrepropertiesFn
lorsque nous invoquonsmakeClass
. Nous devons également être conscients que chaque fonction définie dans une classe peut maintenant recevoir un paramètre après tous ses autres, nommédup
, qui est unSet
qui contient toutes les fonctions qui ont déjà été appelées à la suite de l'appel de la méthode héritée:Ce nouveau style réussit en fait à garantir qu'il
"Construct A"
n'est journalisé qu'une seule fois lorsqu'une instance deBC
est initialisée. Mais il y a trois inconvénients, dont le troisième est très critique :util.invokeNoDuplicates
fonction, et réfléchir à la manière dont ce style évite les invocations multiples n'est pas intuitif et induit des maux de tête. Nous avons également cedups
paramètre embêtant , qui doit vraiment être défini sur chaque fonction de la classe . Aie.NiftyClass
remplace une fonctionniftyFunction
, et utiliseutil.invokeNoDuplicates(this, 'niftyFunction', ...)
pour l'exécuter sans duplicate-invocation,NiftyClass.prototype.niftyFunction
appellera la fonction nomméeniftyFunction
de chaque classe parent qui la définit, ignorera toutes les valeurs de retour de ces classes et exécutera enfin la logique spécialisée deNiftyClass.prototype.niftyFunction
. C'est la seule structure possible . SiNiftyClass
hériteCoolClass
etGoodClass
, et que ces deux classes parentes fournissentniftyFunction
leurs propres définitions,NiftyClass.prototype.niftyFunction
il ne sera jamais (sans risquer de multiples invocations) de:NiftyClass
premier, puis la logique spécialisée des classes-parentsNiftyClass
à tout moment autre que lorsque toute la logique parent spécialisée est terminéeniftyFunction
tout à faitBien sûr, nous pourrions résoudre chaque problème lettré ci-dessus en définissant des fonctions spécialisées sous
util
:util.invokeNoDuplicatesSubClassLogicFirst(instance, fnName, ...)
util.invokeNoDuplicatesSubClassAfterParent(parentName, instance, fnName, ...)
(OùparentName
est le nom du parent dont la logique spécialisée sera immédiatement suivie par la logique spécialisée des classes enfants)util.invokeNoDuplicatesCanShortCircuitOnParent(parentName, testFn, instance, fnName, ...)
(dans ce castestFn
recevrait le résultat de la logique spécialisée pour le parent nomméparentName
, et renverrait unetrue/false
valeur indiquant si le court-circuit devrait se produire)util.invokeNoDuplicatesBlackListedParents(blackList, instance, fnName, ...)
(Dans ce cas ,blackList
serait unArray
des noms de parents dont la logique spécialisée devrait être tout à fait sautée)Ces solutions sont toutes disponibles, mais c'est un chaos total ! Pour chaque structure unique qu'un appel de fonction hérité peut prendre, nous aurions besoin d'une méthode spécialisée définie sous
util
. Quel désastre absolu.Dans cet esprit, nous pouvons commencer à voir les défis de la mise en œuvre d'un bon héritage multiple. L'implémentation complète de
makeClass
j'ai fournie dans cette réponse ne prend même pas en compte le problème d'invocation multiple, ou de nombreux autres problèmes qui se posent concernant l'héritage multiple.Cette réponse devient très longue. J'espère que l'
makeClass
implémentation que j'ai incluse est toujours utile, même si elle n'est pas parfaite. J'espère également que toute personne intéressée par ce sujet aura acquis plus de contexte à garder à l'esprit lors de la lecture plus approfondie!la source
Jetez un œil au package IeUnit .
Le concept d'assimilation implémenté dans IeUnit semble offrir ce que vous recherchez de manière assez dynamique.
la source
Voici un exemple de chaînage de prototype utilisant des fonctions de constructeur :
Ce concept utilise la définition de Yehuda Katz d'une "classe" pour JavaScript:
Contrairement à l' approche Object.create , lorsque les classes sont construites de cette manière et que nous voulons créer des instances d'une «classe», nous n'avons pas besoin de savoir de quoi chaque «classe» hérite. Nous utilisons juste
new
.L'ordre de priorité doit avoir un sens. Tout d'abord, il regarde dans l'objet instance, puis c'est le prototype, puis le prototype suivant, etc.
Nous pouvons également modifier les prototypes qui affecteront tous les objets construits sur la classe.
J'en ai initialement écrit une partie avec cette réponse .
la source
child
hérite deparent1
etparent2
). Votre exemple ne parle que d'une chaîne.Un retardataire dans la scène est SimpleDeclare . Cependant, lorsque vous traitez avec l'héritage multiple, vous vous retrouverez toujours avec des copies des constructeurs d'origine. C'est une nécessité en Javascript ...
Merc.
la source
J'utiliserais ds.oop . C'est similaire à prototype.js et autres. rend l'héritage multiple très facile et minimaliste. (seulement 2 ou 3 ko) Prend également en charge d'autres fonctionnalités intéressantes telles que les interfaces et l'injection de dépendances
la source
Que diriez-vous de cela, il implémente l'héritage multiple en JavaScript:
Et voici le code de la fonction utilitaire specialize_with ():
C'est du vrai code qui s'exécute. Vous pouvez le copier-coller dans un fichier html et l'essayer vous-même. Ça marche.
C'est l'effort pour implémenter MI en JavaScript. Pas beaucoup de code, plus de savoir-faire.
N'hésitez pas à consulter mon article complet à ce sujet, https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS
la source
Je viens d'attribuer les classes dont j'ai besoin dans les propriétés des autres et d'ajouter un proxy pour les pointer automatiquement, j'aime:
la source