Émuler super en javascript
Fondamentalement, est-il un bon mécanisme élégant pour émuler super
avec la syntaxe est aussi simple que l'une des opérations suivantes
this.$super.prop()
this.$super.prop.apply(this, arguments);
Critères à respecter sont :
this.$super
doit être une référence pour le prototype. c'est à dire si je change le super prototype au moment de l'exécution de ce changement pourra être répercuté. Ceci signifie fondamentalement, il le parent a une nouvelle propriété, alors cela devrait être indiqué au moment de l'exécution sur tous les enfants par le biais desuper
comme une codés en dur de référence à la société mère de refléter les changementsthis.$super.f.apply(this, arguments);
doivent travailler pour les appels récursifs. Pour toute enchaînés ensemble de l'héritage où plusieurs super les appels sont faits que vous allez en haut de la chaîne d'héritage, vous ne devez pas frapper le récursive problème.- Vous ne devez pas coder en dur des références à des super objets de vos enfants. I. e.
Base.prototype.f.apply(this, arguments);
défaites le point. - Vous ne devez pas utiliser un X à compilateur JavaScript ou des scripts JavaScript préprocesseur.
- Doit être ES5 conforme
L'implémentation naïve serait quelque chose comme ceci.
var injectSuper = function (parent, child) {
child.prototype.$super = parent.prototype;
};
Mais ce les sauts de la condition 2.
Le plus élégant mécanisme que j'ai vu à ce jour est IvoWetzel de eval
hack, qui est à peu près un JavaScript préprocesseur et échoue donc les critères 4.
- juste curieux, mais est-il une raison pourquoi vous avez besoin d'émuler
super
en JavaScript? compte tenu de la façon dont la chaîne de prototype fonctionne, il me semble complètement inutile. super
est simplement une construction pour le code de la réutilisation. C'est la seule chose qui me manque dans OO sucre pour l'ES5. ES6 apportesuper
et je suis impatient de ce- Votre naïveté de mise en œuvre de casse également les critères 1.
- comment?
injectSuper(P, C); x = new C(); C.prototype = {}; alert(x.$super.constructor === P); y = new C(); alert(y.$super === undefined);
Émet également vrai pour les deux.- bien sûr, il n'. condition on a seulement besoin de refléter les changements apportés à
P
. Si vous écrasezC.prototype
puis sa rupture. ce comportement est normal et c'est stupide d'écrire du code comme ça - J'ai mal compris ce que tu voulais dire par la modification de la super prototype. Il peut être compris de la façon dont j'ai pensé à l'origine, dans le sens que vous êtes littéralement changer pour quelque chose d'autre au lieu de modifier ce qui existe déjà.
- J'aime la façon dont vous critiquez chaque réponse, mais vous n'avez vraiment aucune idée de comment le faire vous-même. Parler de la critique constructive euh?
- o/ Oui, je suis sévère. J'ai eu un bon coup d'oeil à la façon de le faire, et j'ai trouvé tout un tas de pièges. Je contribue activement à la liste des pièges et d'autres personnes encore à l'étape en eux. Donc oui, ils arrivent à être critiqué.
- peut-être que ce est pourquoi la plupart donnent sur l'émulation "classique" de l'héritage en javascript. En fin de compte vous lier le code dans les noeuds pour répondre à des critères pour le classique OO sans vraiment de la résolution d'un pracitcal problème (à moins que la question était de mettre en œuvre classique OO pour l'amour d'elle).
- Ce n'est pas "classique" de l'héritage en tant que tel, plus d'un code de ré-utiliser le mécanisme. Et il peut être fait, vous avez juste à remplacer chaque méthode unique de lexicalement le lier à un bon de la valeur de super. En gros, c'est en essayant d'imiter ES6
super
. - J'ai fait un code qui a passé tous les tests et qu'il cesse de répondre dans les commentaires... Downvote
- Vous pouvez utiliser
Function.call
facilement dans ECMAScript5, jetez un oeil ici: stackoverflow.com/questions/7486825/javascript-inheritance/...
Vous devez vous connecter pour publier un commentaire.
Je ne pense pas qu'il y est "libre" moyen de sortir de la "récursive super" problème que vous mentionnez.
On peut pas jouer avec les
this
, car cela aurait pour effet soit de nous forcer à changer de prototypes dans un autre chemin, ou nous déplacer jusqu'le proto de la chaîne, de perdre des variables d'instance. Par conséquent, l'actuel "classe" et "super-classe" doit être connue lorsque nous ne le super-ing, sans passage que la responsabilité dethis
ou de l'une de ses propriétés.Il y a beaucoup de certaines choses qu'on pourrait essayer de faire mais tout ce que je pense avoir une certaine undesireable conséquences:
Ajouter des informations supplémentaires lors de l'appel de la méthode super
Cela nous oblige à dupliquer l'actuel nom de la classe (si nous pouvons trouver sa super-classe dans un super dictionnaire), mais au moins il n'est pas aussi mauvais que d'avoir à dupliquer le nom de la superclasse, (depuis couplage contre les relations d'héritage si pire que de l'intérieur de couplage avec une classe propre nom)
Alors que c'est loin d'être idéale, il y a au moins certains précédents historiques de 2.x de Python. (Ils "fixe" pour le super 3.0 de sorte qu'il ne nécessite pas d'arguments plus, mais je ne suis pas sûr de la quantité de magie que les impliqués et comment portable il serait JS)
Edit: Travail violon
John Resig posté un ineherence mécanisme simple, mais grande
super
de soutien.La seule différence est que
super
points à la méthode de base de l'endroit où vous êtes en l'appelant.Prendre un coup d'oeil à http://ejohn.org/blog/simple-javascript-inheritance/.
_super
avant et après l'appel de la méthode.try { var ret = fn.apply(this, arguments); } catch (e) { this._super = tmp; throw e; } this._super = tmp;
_super
dans une méthode du même nom. Boo.P
etC
, oùC
hérite deP
. Supposons égalementP
etC
à la fois de définir leur proprefoo
etbar
méthodes sur leurs prototypes. Enfin, supposons queP.foo
appelsbar
etC.foo
appels_super
. Lors de l'appel denew C().foo()
,C.bar
est appelée, pasP.bar
. Sauf si vous êtes vraiment intelligent, c'est un bug. Je n'ai même pas essayé la récursivité mutuelle entre le parent et l'enfant, les méthodes de la classe. Je parie que c'est encore plus méchant que je parie que c'totalement casse son_super
mécanisme.Noter que pour la suite de la mise en œuvre, lorsque vous êtes à l'intérieur d'une méthode qui est appelée via
$super
, l'accès aux propriétés tout en travaillant dans la classe parent ne jamais résoudre pour les enfants de la classe de méthodes ou variables, sauf si vous accédez à un membre qui est stocké directement sur l'objet lui-même (par opposition à joint pour le prototype). Cela évite un tas de confusion (lire aussi subtile bugs).Mise à jour:
Voici une application qui fonctionne sans
__proto__
. Le hic, c'est que l'utilisation de$super
est linéaire en le nombre de propriétés de l'objet parent a.La suite de la mise en œuvre est pour les navigateurs qui prennent en charge les
__proto__
propriété:Exemple:
.__proto__
est non-standard. Je crains que je ne peux pas utiliser cette solution.foo
etbar
définis à la fois le parentP
et de l'enfant de la classeC
.P.foo
appelsbar
. Supposons maintenant queC.foo
appelsfoo
à l'aide de la super mécanisme. Je n'ai pas testé, mais je crois Resig du codeP.foo
appelC.bar
. C'est risqué de l'OMI. Je ne vais pas écrire un scénario de test, mais si quelqu'un, s'il vous plaît laissez-moi savoir la suite.Object.create
lors de la création du prototype.__proto__
version. C'est la seule façon de vraiment imiter lesuper
comportement de langues classiques...Parent.call(this, arg1, arg2, ...)
dans leChild
constructeur de la première ligne.const sleep = t => new Promise(r => setTimeout(r, t)); B.prototype.foo = async function () { await sleep(1); return this.$super("foo")() + "_B2"; };
La principale difficulté de la
super
est que vous devez trouver ce que j'appellehere
: l'objet qui contient la méthode qui fait de la super référence. Qui est absolument nécessaire pour obtenir le droit de la sémantique. Évidemment, en ayant le prototype dehere
est tout aussi bon, mais cela ne fait pas beaucoup de différence. La suite est une solution statique:"me"
en elle. J'avais aussi valoir que l'super(B).method
est mieux, puisme.super.method
.A.prototype.method
fait référence à cette méthode, devrait A ou B de la méthode-elle être invoquée? Laquelle est la bonne?(function me() { console.log(me) }()); console.log(me);
JsFiddle:
Ce qui est mal à cela?
Utilisation:
Exemple d'utilisation privilégiée avec les méthodes à la place des méthodes publiques.
Dans l'esprit de l'exhaustivité (aussi merci à vous tous pour ce fil, il a été un excellent point de référence!) Je voulais lancer dans cette mise en œuvre.
Si nous sommes d'admettre qu'il n'y a pas de bonne façon de répondre à tous les critères ci-dessus, puis je pense que c'est un vaillant effort par le Salsifis équipe (je viens de le trouver) trouvé ici. C'est la seule application que j'ai vu qui évite le problème de récursivité, mais permet aussi de
.super
être une référence à la bonne prototype, sans pré-compilation.Donc, au lieu de casser les critères 1, nous rompons 5.
la technique repose sur l'aide de
Function.caller
(pas es5 conforme, même si elle est largement pris en charge dans les navigateurs et es6 supprime le besoin futur), mais ça donne vraiment de solution élégante pour toutes les autres questions (je pense)..caller
nous permet d'obtenir la référence à une méthode qui nous permet de localiser l'endroit où nous sommes dans la chaîne de prototype, et utilise ungetter
pour retourner le bon prototype. Son pas parfait, mais il est largement solution différente de ce que j'ai vu dans cet espacevous pouvez également régler ce le retour de la bonne méthode (comme dans le post original) ou vous pouvez supprimer le besoin d'annoter chaque méthode avec le même nom, en passant le nom de la super fonction (au lieu d'utiliser un getter)
ici est un travail de violon démonstration de la technique: jsfiddle
Ont un look à la Élégant de la bibliothèque; il fournit des classes et l'héritage et l'accès à une méthode substituée à l'aide de
this.$super
this.$super
à la "bonne valeur" avant et après. Il a également avale des erreurs 🙁Pour ceux qui ne comprennent pas la récursivité problème de l'OP, voici un exemple:
La raison:
this
en Javascript est dynamiquement liée.B.prototype = new A();
est assez mauvais pour moi.B.prototype = Object.create(A.prototype)
this
contenant toujours des références à l'enfant de la classe des méthodes.this.$super.foo.call(this, ...)
appels àthis.$super.foo.call(this.$super, ...)
. Et vous devriez obtenir 3, et en passant par la Classe.étendre l'exemple ci-dessus, la commutation d'Un retour -n devrait également vous obtenir -3. Encore une fois, ne suggère pas parce que vous perdez votre C exemple lorsque l'intérieur de A et B et qui peut ne pas être ce que vous attendez, je voulais juste illustrer un chemin qui fait qu'appeler de travail à la chaîne. 🙂Je suis venu avec un moyen qui vous permettra d'utiliser un pseudo-mot-clé Super en changeant le contexte d'exécution (d'Une façon dont je l'ai pas encore vu d'être présenté ici.) L'inconvénient que j'ai trouvé que je ne suis pas heureux du tout, c'est qu'il ne peut pas ajouter le "Super" variable à la méthode du contexte d'exécution, mais au lieu de cela, elle remplace l'ensemble du contexte d'exécution, cela signifie que toutes les méthodes privées défini avec la méthode de est devenu indisponible...
Cette méthode est très similaire à la "eval hack" OP présentés toutefois, il ne faut pas faire le traitement sur la fonction de la chaîne source, juste redeclares la fonction à l'aide de la fonction eval dans le contexte d'exécution en cours. En faisant un peu mieux que les deux méthodes ont le même inconvénient précité.
Méthode très simple:
Un exemple de rendre une classe
Un exemple de l'inconvénient mentionné plus tôt.
Notez également que cette méthode n'est pas "superifying" l'enfant du constructeur sens que Super n'est pas disponible pour elle. Je voulais juste expliquer le concept, de ne pas faire de travail de la bibliothèque 🙂
Aussi, viens de réaliser que j'ai rencontré tous les de l'OP conditions! (Même si il devrait vraiment être une condition sur le contexte d'exécution)
Voici ma version: lowclass
Et voici le
super
spaghetti soupe à l'exemple de l'test.js fichier (EDIT: en fait en cours d'exécution par exemple):JS:
HTML:
Essayer invalide membre de l'accès ou de l'utilisation incorrecte de
_super
lèvera une erreur.Sur les exigences requises:
cela.$super doit être une référence pour le prototype. c'est à dire si je change le super prototype au moment de l'exécution de ce changement pourra être répercuté. Ceci signifie fondamentalement, il le parent a une nouvelle propriété, alors cela devrait être indiqué au moment de l'exécution sur tous les enfants par le biais de super tout comme une codés en dur de référence à la société mère de refléter les changements
Non, le
_super
helper ne retourne pas le prototype, seulement un objet copié descripteurs d'éviter la modification du privé et protégé des prototypes. En outre, le prototype à partir de laquelle les descripteurs sont copiés à partir de est tenu dans le champ d'application de laClass
/subclass
appel. Il serait bien de l'avoir. FWIW, natifclass
es se comportent de la même.cela.$super.f.appliquer(ce qui, arguments); fonctionne pour les appels récursifs. Pour toute enchaînés ensemble de l'héritage où plusieurs super les appels sont faits que vous allez en haut de la chaîne d'héritage, vous ne devez pas frapper le récursive problème.
yep, pas de problème.
Vous ne devez pas coder en dur des références à des super objets de vos enfants. I. e. De la Base.le prototype.f.appliquer(ce qui, arguments); défaites le point.
yep
Vous ne devez pas utiliser un X à compilateur JavaScript ou des scripts JavaScript préprocesseur.
yep, tous les runtime
Doit être ES5 conforme
Oui, il comprend une Babel de génération basé sur l'étape (f.e. lowclass utilise WeakMap, qui est compilé pour un non-perméable ES5 forme). Je ne pense pas que cela va à l'encontre exigence 4, il vient me permet d'écrire ES6+ mais il doit encore travailler dans l'ES5. Certes je n'ai pas fait beaucoup de test de cette ES5, mais si vous souhaitez l'essayer, nous pouvons certainement le fer des builds questions sur ma fin, et à partir de votre fin, vous devriez être en mesure de consommer sans toutes les étapes de génération.
La seule exigence qui n'a pas rencontré est de 1. Il serait agréable. Mais peut-être que c'est une mauvaise pratique à échanger des prototypes. Mais en fait, j'ai utilise où je voudrais échanger des prototypes pour réaliser des méta choses. 'Twould être agréable d'avoir cette fonctionnalité native
super
(qui est statique 🙁 ), le laisser seul dans cette mise en œuvre.Pour vérifier la condition 2, j'ai ajouté à la base récursive test pour mon test.js qui fonctionne (EDIT: en fait en cours d'exécution par exemple):
JS:
HTML:
(la classe d'en-tête est un peu long pour ces petites classes. J'ai quelques idées pour le rendre possible de réduire ce à dire, sinon que tous les assistants sont nécessaires jusqu'à l'avant)
Je pense que j'ai un moyen plus simple....
new Father().say.apply(this)
contourner est terrible.