IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

[OO] Implémentation de l'héritage ET d'appels super()


Sujet :

JavaScript

  1. #61
    Membre Expert Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    En fait non, fausse alerte, rien de bizarre.
    Je viens de comprendre comment on simulait la présence de variables privées : ils ne font pas partie de l'instance mais appartiennent au contexte du constructeur, donc il n'y a pas de raisons d'utiliser this pour les atteindre.Par contre, je rencontre encore un soucis (décidément ).
    J'ai l'impression que la surcharge effectuée par extends n'est pas complete.
    Visiblement, tout appel à une méthode surchargée depuis une classe parent (là ou la première méthode est déclarée) appelle en fait la méthode non surchargée.
    Si on appelle ensuite la même méthode mais depuis la classe enfant, la version surchargée est ici appelée.
    Pourquoi le this ne représente pas l'instance en cours ici?
    En fait, le this représente l'objet en cours, toujours le même quel que soit la méthode (parente ou non) appelée, le seul moyen de lié l'objet à un niveau d'héritage est de stocker ce niveau quelque part comme par exemple dans les méthodes elles-même (lors de l'héritage par exemple) et de tester ce niveau pour appeler les bonnes méthodes (par exemple avec une condition dans une méthode qu'on aura écrite "self" ou "super").

    Sinon il y a une erreur dans le code que tu nous a fournis (mais j'imagine qu'elle n'est pas présente dans ton code sinon rien ne fonctionnerait) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    var Parent = function (){
     ...
    }

    ==>>>

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    var Parent = function (){
     ...
    });
    Bon, je m'y remets et je te cherche une solution simplifiée.

    nb: pour la retranscription non-optimale du code d'un langage vers un autre, c'est pour des multiples raisons, casse-tête à le faire fonctionner, code non-adapté au langage, etc.... simplement réécrire une version javascript dans les normes et plus logique, avec qqes "remplacer" dans une éditeur de texte ça ne devrait pas être trop compliqué, et la découpe procédurale (des fonctions) devrait aisément te permettre de garder ton code à jour (par rapport aux autres langages) en ne modifiant que certains petits brols.

  2. #62
    Membre Expert Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Par défaut
    pour corriger ton problème, tu devrais modifier :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var newFN = function(){
    		parent.apply(this, arguments);
     
    		// Gestion de super
    		this.__super = this.__super ? {__super:this.__super} : {};
    		for(var i in this){
    			if(typeof this[i] == "function"){
    				this.__super[i] = this[i];
    			}
    		}
     
    		var result = FN.apply(this, arguments);
     
    		return result;
    	};
    par un truc du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    var newFN = function () {
            parent.apply(this, arguments);
     
            // Gestion de super
            this.__super = this.__super ? {__super: this.__super} : {};
     
            // sauvegarde de l'instance courante
            var that = this;
     
            // sauvegarde des fonctions parentes
            for (var i in this) {
                if (typeof this[i] == "function") {
                    this.__super[i] = (function (f) {
                        // creation d'une nouvelle fonction identique à celle du parent mais qui sera appliqué sur l'objet "that", donc la bonne instance
                        var s = function () {
                                f.apply(that, arguments);
                            };
                        // copie des statiques de la méthode parente "f" dans la nouvelle méthode "s"
                        for (var i in f) {
                            s[i] = f[i];
                        }
                        // renvoie à "this.__super[i]" la nouvelle méthode "s"
                        return s;
                    })(this[i]);
                }
            }
     
            var result = FN.apply(this, arguments);
     
            return result;
        };
    edit: en espérant que tu n'aies pas de nouveaux constructeurs (fonctions qui pourraient être appelée précédée d'un new) au sein même des tes "classes" (enfant ou parent) car dans ce cas ça complexifierait la ligne suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     var s = function () {
                                f.apply(that, arguments);
                            };
    il faudrait tester si "this == instanceof arguments.callee" et dans ce cas, créer de nouveau une nouvelle fonction qui instancierait un nouvel objet de la fonction de base tout en copiant une fois de plus son prototype et ses membres statiques.


    edit2: woaw, après avoir testé, ça fonctionne du premier coup, je suis trop fier de moi :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    __construct enfant
    enfant
    parent
    __construct parent
    enfant
    parent

  3. #63
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Citation Envoyé par Willpower Voir le message
    En fait, le this représente l'objet en cours, toujours le même quel que soit la méthode (parente ou non) appelée.
    Je n'ai pas eu cette impression.
    En reprenant mon exemple avec le grandpere, le pere et l'enfant, suivant que je fasse un alert (this.toSource()); je n'obtiens pas les mêmes méthodes. On dirait que suivant la classe de déclaration de la méthode, on ne tape pas la même chose (puisque les méthodes sont tantôt déclarées et à d'autres endroits non).
    En fait on obtiens pas un objet qui est la compilation verticale de la hiérarchie, comme on serait tenté de le penser avec un langage OO standard.
    Mais c'est surement normal : JS n'est pas un langage OO, on l'aura compris.

    Sinon il y a une erreur dans le code que tu nous a fournis (mais j'imagine qu'elle n'est pas présente dans ton code sinon rien ne fonctionnerait) :
    Oui en effet je l'ai corrigé dans mon message merci.
    J'ai du aller un peu vite.


    nb: pour la retranscription non-optimale du code d'un langage vers un autre, c'est pour des multiples raisons, casse-tête à le faire fonctionner, code non-adapté au langage, etc.... simplement réécrire une version javascript dans les normes et plus logique, avec qqes "remplacer" dans une éditeur de texte ça ne devrait pas être trop compliqué, et la découpe procédurale (des fonctions) devrait aisément te permettre de garder ton code à jour (par rapport aux autres langages) en ne modifiant que certains petits brols.
    Je te dirais ça à l'usage si l'expérience t’intéresse. Pour l'instant tu as amplement raison c'est casse-tête. Par contre j’espère faire un bon investissement pour l'avenir.

    Citation Envoyé par Willpower Voir le message
    pour corriger ton problème, tu devrais modifier
    [...]

    par un truc du genre :
    J'ai un "too much récursion" quand j’appelle une méthode du GrandPere depuis une méthode de la classe Père elle-même appelée par le super de la classe Enfant.
    Par contre avec que deux niveaux ca fonctionne en effet, bien joué!

    Je ne trouve pas l'origine de ce bouclage, tout me semble logiquement lié du niveau N vers N-1...

    edit: en espérant que tu n'aies pas de nouveaux constructeurs (fonctions qui pourraient être appelée précédée d'un new) au sein même des tes "classes" (enfant ou parent) car dans ce cas ça complexifierait la ligne suivante :
    Au niveau des constructeur, j'ai résolu mes problèmes à la "brutale".

    J'ai ajouté une méthode nEw() au prototype de Function :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    Function.prototype.nEw = function (){
    	var instance = new this ();
    	instance.__construct.apply(instance, arguments);
     
    	return instance;
    }
    Vu qu'il était trop compliqué de gérer la construction "logique" pendant la construction "javascript", je me suis dit qu'il fallait laisser JS faire son travail tranquillement et appeler "__construct" (le constructeur "logique") à la suite comme une simple méthode publique.
    A noter que je suis susceptible d'appeler this.__super.__construct sur tous les niveau de la hiérarchie et j'ai pu tester ta nouvelle fonction sur 5 niveaux de classe (-> apparition d'une récursivité infinie dès le 2ième niveau).

    Je ne touche plus au process de construction JS donc.


    Merci pour ta réponse en tout cas.

  4. #64
    Invité
    Invité(e)
    Par défaut
    salut,

    c'est possible de savoir en quoi tu pourrais avoir besoin de traduire un langage dans pleins de versions style php, java ou javascript?

    juste par curiosité..

  5. #65
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Je ne sais plus si je l'ai déjà évoqué dans le début du topic, mais je maintient seul un ensemble conséquent de classes PHP constituant un ORM maison.
    Beaucoup plus que le simple ORM, j'ai monté différentes sur-couches pour obtenir un système d'accès aux données gérant la sécurité, l'intégrité structurelle (le relationnel est géré par le SGBDR), le formattage, etc...

    Désormais je veux le traduire en d'autres langages pour chainer mes objets et accéder à mes données au travers de services HTTP sur un client qui communique avec un ou plusieurs serveurs (PHP c'est pas très adapté au côté client. Java, Javascript et AS3 beaucoup plus).
    Le fait que je sois seul est sensiblement important : j'ai pas envie de maintenir n architectures, n conventions de nommage et n hiérarchies de classes.
    C'est un confort que je m'offre, pas une obligation. Après tout, j'aurai pu faire tout ca en procédural mais je n'aurais pas vu un stade aussi avancé en si peu de temps je pense.

  6. #66
    Invité
    Invité(e)
    Par défaut
    mm je vois l'idée à peu près.

    Je maintiens mon avis.
    Si tu me dis que t'as un parseur qui te déclines ton orm dans plusieurs langages je dis chapô c'est bastos.

    Mais si c'est pour traduire d'un langage vers un autre avec peut de différences, je trouve que c'est dommage et ca force qd même à y aller à la mano. Mais bon, le temps toussa, au moins l'idée à l'air cool

  7. #67
    Membre Expert Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Je n'ai pas eu cette impression.
    En reprenant mon exemple avec le grandpere, le pere et l'enfant, suivant que je fasse un alert (this.toSource()); je n'obtiens pas les mêmes méthodes. On dirait que suivant la classe de déclaration de la méthode, on ne tape pas la même chose (puisque les méthodes sont tantôt déclarées et à d'autres endroits non).
    En fait on obtiens pas un objet qui est la compilation verticale de la hiérarchie, comme on serait tenté de le penser avec un langage OO standard.
    Mais c'est surement normal : JS n'est pas un langage OO, on l'aura compris.
    je n'avais pas encore lu le code en disant que le "this" ne change pas, tu copies les méthodes dans un nouvel objet appelé '__super' donc forcément lorsque tu appelles ces méthodes tu les appelles sur cet objet '__super' et non plus sur le this courant. la solution est de créer une méthode super qui appliquera la bonne méthode sur le bon objet suivant tes désires.



    Citation Envoyé par fanfouer Voir le message
    J'ai un "too much récursion" quand j’appelle une méthode du GrandPere depuis une méthode de la classe Père elle-même appelée par le super de la classe Enfant.
    Par contre avec que deux niveaux ca fonctionne en effet, bien joué!
    Comme dit au dessus, si tu appelles __super, ça appelera toujours le __super du même this, donc juste la classe parente du dernier enfant et jamais les grand-père, d'où ta fonction super à créer.

  8. #68
    Membre Expert Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Par défaut
    Ce n'est pas très propre, je l'ai fait à l'arrache(donc il doit y avoir bien mieux) mais en attendant ça fonctionne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    Function.prototype.extends = function(FN){
    	var parent = this;
     
    	var newFN = function () {
            parent.apply(this, arguments);
     
            // Gestion de super
            this.__super = this.__super ? {__super: this.__super} : {};
     
            // sauvegarde de l'instance courante
            var that = this;
     
            // sauvegarde des fonctions parentes
            for (var i in this) {
                if (typeof this[i] == "function") {
                    this.__super[i] = (function (f) {
                        // creation d'une nouvelle fonction identique à celle du parent mais qui sera appliqué sur l'objet "that", donc la bonne instance
                        var s = function () {
                                f.apply(that, arguments);
                            };
                        // copie des statiques de la méthode parente "f" dans la nouvelle méthode "s"
                        for (var i in f) {
                            s[i] = f[i];
                        }
                        // renvoie à "this.__super[i]" la nouvelle méthode "s"
                        return s;
                    })(this[i]);
                }
            }
     
            var result = FN.apply(this, arguments);
     
            return result;
        };
     
    	var proto = function(){};
    	proto.prototype = parent.prototype;
    	newFN.prototype = new proto();
     
    	for(var i in FN.prototype){
    		newFN.prototype[i] = FN.prototype[i];
    	}
     
    	// copie des statiques parents
    	for(var i in parent){
    		newFN[i] = parent[i];
    	}
     
    	// copie(+ecrase) des statiques nouveau
    	for(var i in FN){
    		newFN[i] = FN[i];
    	}
     
    	newFN.prototype.super = function(){
    		var caller = arguments.callee.caller;
    		var obj = this, instance = null;
    		while(obj && !instance){
    			for(var i in obj){
    				if(obj[i] == caller || obj[i] == caller.caller){
    					instance = obj;
    				}
    			}
    			obj = obj.__super;
    		}
    		return instance.__super;
    	};
     
    	newFN.prototype.self = function(){
    		var caller = arguments.callee.caller;
    		var obj = this, instance = null;
    		while(obj && !instance){
    			for(var i in obj){
    				if(obj[i] == caller || obj[i] == caller.caller){
    					instance = obj;
     
    				}
    			}
    			obj = obj.__super;
    		}
    		return instance;
    	};
     
    	newFN.__PARENT = parent;
    	return newFN;
    }
     
    var alert = function(a){document.writeln(a,"<br/>");};
     
    // Parent
    var Parent = function (){
      this.__construct = function (){
        alert ("__construct parent");
        this.self().test();
      }
     
      // Méthodes
      this.test = function(){
        alert ("parent");
      }
    };
    // Enfant
    var Enfant = function (){
      this.__construct = function (){
        alert("__construct enfant");
        this.self().test();
     
        this.super().__construct();
      }
     
      // Méthodes
      this.test = function (){
        alert ("enfant");
        this.super().test();
      }
    };
    // Petit
    var Petit = function (){
      this.__construct = function (){
        alert("__construct petit");
        this.self().test();
     
        this.super().__construct();
      }
     
      // Méthodes
      this.test = function (){
        alert ("petit");
        this.super().test();
      }
    };
     
    var Enfant = Parent.extends(Enfant);
    var Petit = Enfant.extends(Petit);
     
    var instance = new Petit();
    instance.__construct();

  9. #69
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Citation Envoyé par galerien69 Voir le message
    mm je vois l'idée à peu près.
    Je maintiens mon avis.
    Si tu me dis que t'as un parseur qui te déclines ton orm dans plusieurs langages je dis chapô c'est bastos.
    Je suis bien d'accord. Mais en même temps le développement d'un tel parseur est complexe. Et doit être maintenu à jour.
    D'un autre côté, le maintient d'une version dans chaque langage force à utiliser des concepts de POO (dans mon cas) supporté dans tous les langages utilisés et donc probablement standard.
    J'ai du revoir un certain nombre de point pour fournir une première release Java puisque PHP et ses LSB c'est quand même bien particulier.

    Après je suis d'accord de dire qu'on exploite pas toutes les possibilités d'un langage donné, les aspects qui peuvent créer sa richesse. Mais le portage vers Java m'a ouvert les yeux ce qu'il en coutait au niveau conception et théorie d'utiliser les subtilités OO de PHP. Le langage devrait uniquement être là pour aider le développeur à formaliser sa pensée, pas pour la vriller.

    Mais si c'est pour traduire d'un langage vers un autre avec peut de différences, je trouve que c'est dommage et ca force qd même à y aller à la mano. Mais bon, le temps toussa, au moins l'idée à l'air cool
    Ca j'en reparlerai dans quelques années.

    Citation Envoyé par Willpower Voir le message
    Comme dit au dessus, si tu appelles __super, ça appelera toujours le __super du même this, donc juste la classe parente du dernier enfant et jamais les grand-père, d'où ta fonction super à créer
    C'est plus clair pour moi, merci.

    Citation Envoyé par Willpower Voir le message
    Ce n'est pas très propre, je l'ai fait à l'arrache(donc il doit y avoir bien mieux) mais en attendant ça fonctionne
    Tout est okay pour moi en tout cas, je te remercie beaucoup de t'y être replongé.

    Pour ma culture, peux-tu m'expliquer pourquoi c'est "crado"? Moi je trouve ça très bien

    [Edit]

    Tiens j'ai peut-être un exemple de soucis du à ce qui est prétendu "sale".

    L'exemple ci-dessous peut par exemple être trouvé avec des setters. On y introduit des comportements de traitement différents mais complémentaires du parent à l'enfant et le set dans le constructeur peut être vu comme tout autre set. D'autant qu'ici on est obligé de s'en servir vu qu'on a que des variables privées (et non protected).

    Pour moi il prend le caller non comme étant le __construct du parent, mais comme le __construct du petit. Du coup l'instance donnée par le super() dans le test() de l'enfant n'est pas le parent mais... l'enfant lui-même, d'ou le bouclage infini.

    Une idée éventuellement pour éviter qu'il se floute lui-même?
    Bon c'est histoire de chercher la petite bête hain!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
     
    // Parent
    var Parent = function (){
      // Attributs privés
      var _maVar = null;
     
      // Constructeur
      this.__construct = function (val){
        alert ("__construct parent");
     
        this.setVar(val); // /!\ Normalement différent de this.self().test();
      }
     
      // Méthodes
      this.setVar = function(a){
        _maVar = a;
      }
    };
     
    // Enfant
    var Enfant = function (){
      this.__construct = function (val){
        alert("__construct enfant");
     
        this.super().__construct(val); // On est obligé de passer la valeur au constructeur, si on met null et qu'on utilise le setter ici, le parent risque d'avoir le dernier mot et de mettre null dans la variable.
      }
     
      // Méthodes
      this.setVar = function (a){
        alert ("enfant");
     
        // Traitements spéciaux à l'enfant
     
        // Enregistrement
        this.super().setVar(a);
      }
    };
     
    // Petit
    var Petit = function (){
      this.__construct = function (val){
        alert("__construct petit");
     
        this.super().__construct(val);
      }
     
      // Pas de méthodes
    };
     
    var Enfant = Parent.extends(Enfant);
    var Petit = Enfant.extends(Petit);
     
    var instance = new Petit();
    instance.__construct("super");
     
    // On devrait normalement avoir à l'alert :
    // "__construct petit"
    // "__construct enfant"
    // "__construct parent"
    // "enfant"
    // <s>"parent"</s> /!\ Boucle infinie

  10. #70
    Membre Expert Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Pour ma culture, peux-tu m'expliquer pourquoi c'est "crado"? Moi je trouve ça très bien
    déjà, le super peux être trouvé à partir du self sans dupliquer de code. après, je ne suis pas un expert, donc il y a d'office plus simple.


    voici les fonctions self et super commentées (et super appelant simplement self)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    	newFN.prototype.super = function(){
    		return this.self().__super;
    	};
     
    	newFN.prototype.self = function(){
    		var caller = arguments.callee.caller;
    		// si le caller est "super" alors notre fonction "caller" et le caller de "super".
    		if(caller == newFN.prototype.super)
    			caller = caller.caller; // équivaut à : "caller = newFN.prototype.super.caller;"
    		// vérifie si la fonction "caller" appartient au "this" dans quel cas nous sommes dans une méthode de la dernière classe instanciée (enfant le plus bas)
    		for(var i in this)
    			if(this[i] == caller)
    					return this;
    		// sinon on parcours la récursion des objets "__super" pour trouver à quel niveau se trouve la fonction appelante(caller) et retourner l'objet "__super" correspondant.
    		for(var obj = this; obj; obj = obj.__super){
    			for(var i in obj)
    				if(obj[i] == caller.caller) // le caller de caller car nous avons surchargé les fonctions des objets __super (voir dans le code précédent : "var s = function () { f.apply(that, arguments); };"
    					return obj;
    		}
    		// la fonction est introuvable dans l'objet et ses parents
    		return null;
    	};

  11. #71
    Membre Expert Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    [Edit]

    Tiens j'ai peut-être un exemple de soucis du à ce qui est prétendu "sale".

    L'exemple ci-dessous peut par exemple être trouvé avec des setters. On y introduit des comportements de traitement différents mais complémentaires du parent à l'enfant et le set dans le constructeur peut être vu comme tout autre set. D'autant qu'ici on est obligé de s'en servir vu qu'on a que des variables privées (et non protected).

    Pour moi il prend le caller non comme étant le __construct du parent, mais comme le __construct du petit. Du coup l'instance donnée par le super() dans le test() de l'enfant n'est pas le parent mais... l'enfant lui-même, d'ou le bouclage infini.

    Une idée éventuellement pour éviter qu'il se floute lui-même?
    Bon c'est histoire de chercher la petite bête hain!
    le problème vient qu'il exécute le "setVar" comme s'il appartenait à l'instance de "petit" au lieu de "enfant" vu qu'il a pas été écrasé.

    j'ai donc rajouté un peu de code :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    Function.prototype.Extends = function(FN){
    	var parent = this;
     
    	var newFN = function () {
            parent.apply(this, arguments);
     
            // Gestion de super
            this.__super = this.__super ? {__super: this.__super} : {};
     
            // sauvegarde de l'instance courante
            var that = this;
     
            // sauvegarde des fonctions parentes
            for (var i in this) {
                if (typeof this[i] == "function") {
                    this.__super[i] = (function (f) {
                        // creation d'une nouvelle fonction identique à celle du parent mais qui sera appliqué sur l'objet "that", donc la bonne instance
                        var s = function () {
                                f.apply(that, arguments);
                            };
                        // copie des statiques de la méthode parente "f" dans la nouvelle méthode "s"
                        for (var i in f) {
                            s[i] = f[i];
                        }
                        // renvoie à "this.__super[i]" la nouvelle méthode "s"
                        return s;
                    })(this[i]);
                }
            }
     
            var result = FN.apply(this, arguments);
     
            return result;
        };
     
    	var proto = function(){};
    	proto.prototype = parent.prototype;
    	newFN.prototype = new proto();
     
    	for(var i in FN.prototype){
    		newFN.prototype[i] = FN.prototype[i];
    	}
     
    	// copie des statiques parents
    	for(var i in parent){
    		newFN[i] = parent[i];
    	}
     
    	// copie(+ecrase) des statiques nouveau
    	for(var i in FN){
    		newFN[i] = FN[i];
    	}
     
    	newFN.prototype.Super = function(){
    		return this.Self().__super;
    	};
     
    	newFN.prototype.Self = function(){
    		var caller = arguments.callee.caller;
    		// si le caller est "super" alors notre fonction "caller" et le caller de "super".
    		if(caller == newFN.prototype.Super)
    			caller = caller.caller; // équivaut à : "caller = newFN.prototype.super.caller;"
     
    		var instance = null;
     
    		// vérifie si la fonction "caller" appartient au "this" dans quel cas nous sommes dans une méthode de la dernière classe instanciée (enfant le plus bas)
    		for(var i in this)
    			if(this[i] == caller)
    					instance = this;
     
    		// sinon on parcours la récursion des objets "__super" pour trouver à quel niveau se trouve la fonction appelante(caller) et retourner l'objet "__super" correspondant.
    		for(var obj = this; obj; obj = obj.__super){
    			for(var i in obj)
    				if(obj[i] == caller.caller) // le caller de caller car nous avons surchargé les fonctions des objets __super (voir dans le code précédent : "var s = function () { f.apply(that, arguments); };"
    					instance = obj;
    		}
     
    		return instance; 
    	};
     
    	newFN.__PARENT = parent;
    	return newFN;
    }
     
     
     
     
    var alert = function(a){document.writeln(a,"<br/>");};
    // Parent
    var Parent = function (){
      // Attributs privés
      var _maVar = null;
     
      // Constructeur
      this.__construct = function (val){
        alert ("__construct parent");
     
        this.setVar(val); // /!\ Normalement différent de this.self().test();
      }
     
      // Méthodes
      this.setVar = function(a){
        alert ("parent");
        _maVar = a;
      }
    };
     
    // Enfant
    var Enfant = function (){
      this.__construct = function (val){
        alert("__construct enfant");
     
        this.Super().__construct(val); // On est obligé de passer la valeur au constructeur, si on met null et qu'on utilise le setter ici, le parent risque d'avoir le dernier mot et de mettre null dans la variable.
      }
     
      // Méthodes
      this.setVar = function (a){
        alert("enfant");
     
        // Traitements spéciaux à l'enfant
     
        // Enregistrement
        this.Super().setVar(a);
      }
    };
     
    // Petit
    var Petit = function (){
      this.__construct = function (val){
        alert("__construct petit");
     
        this.Super().__construct(val);
      }
     
      // Pas de méthodes
    };
     
    var Enfant = Parent.Extends(Enfant);
    var Petit = Enfant.Extends(Petit);
     
    var instance = new Petit();
    instance.__construct("super");
    par contre il persiste un problème, la fonction "setVar" est appelée 2 fois, une fois pour "Petit" et une fois pour "Enfant", c'est déjà mieux qu'une infinité de fois, mais je ne comprends pas encore comment ça se fait.


    j'ai aussi modifié les noms "extends", "super" et "self" en "Extends", "Super" et "Self" car IE provoque des erreurs car ces noms sont réservés.

  12. #72
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Citation Envoyé par Willpower Voir le message
    par contre il persiste un problème, la fonction "setVar" est appelée 2 fois, une fois pour "Petit" et une fois pour "Enfant", c'est déjà mieux qu'une infinité de fois, mais je ne comprends pas encore comment ça se fait.
    Très juste je constate la même chose.

    Peut-est-ce du au fait qu'au premier appel il résoud super() comme étant l'Enfant lui-même (donc il va la ré-exécuter une deuxième fois) et au second coup, super() est bien résolu comme étant le Parent.

    Par contre je vois pas très bien comment palier au problème (qui dans le cas d'un simple setter n'en est pas un. Par contre dans d'autres cas, probablement).

    j'ai aussi modifié les noms "extends", "super" et "self" en "Extends", "Super" et "Self" car IE provoque des erreurs car ces noms sont réservés.
    Très bonne idée, je n'ai pas encore testé sous IE, c'est bien si sa fonctionne correctement du coup.

    Un immense merci pour l'aide apportée

  13. #73
    Membre Expert Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Par défaut
    Voila, problèmes corrigés, ça affiche bien :

    __construct petit
    __construct enfant
    __construct parent
    enfant
    parent

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
     
    Function.prototype.Extends = function (FN) {
        var parent = this;
    	// crée une nouvelle fonction qui exécutera la parente, sauvegardera ses champs dans __super puis exécutera la fonction enfant
        var newFN = function () {
    			// applique la fonction parente
                parent.apply(this, arguments);
     
                // Gestion de super
                this.__super = this.__super ? {
                    __super: this.__super
                } : {};
     
                // sauvegarde de l'instance courante
                var that = this;
     
                // sauvegarde des fonctions parentes
                for (var i in this) {
                    if (typeof this[i] == "function") {
    					// exécute une fonction anonyme pour sauvergarder this[i] dans this.__super[i]
                        this.__super[i] = (function (f) {
                            // si la fonction original à déjà été surchargé en imposant le this alors on ne fait rien
                            if (f.__original) return f;
                            // creation d'une nouvelle fonction identique à celle du parent mais qui sera appliqué sur l'objet "that", donc la bonne instance
                            var s = function () {
                                    f.apply(that, arguments);
                                };
                            // copie des statiques de la méthode parente "f" dans la nouvelle méthode "s"
                            for (var i in f) {
                                s[i] = f[i];
                            }
                            // on sauvergarde la fonction originale f dans la nouvelle s
                            s.__original = f;
                            // renvoie à "this.__super[i]" la nouvelle méthode "s"
                            return s;
                        })(this[i]);
                    }
                }
    			// applique la fonction enfant
                return FN.apply(this, arguments);
            };
     
    	// attribution du prototype parent à la nouvelle fonction => (instanceof parent == true && instanceof newFN == true)
        var proto = function () {};
        proto.prototype = parent.prototype;
        newFN.prototype = new proto();
     
    	// copie des prototypes enfants sur la nouvelle fonction
        for (var i in FN.prototype) {
            newFN.prototype[i] = FN.prototype[i];
        }
     
        // copie des statiques parents
        for (var i in parent) {
            newFN[i] = parent[i];
        }
     
        // copie(+ecrase) des statiques nouveau
        for (var i in FN) {
            newFN[i] = FN[i];
        }
     
        // definition de Super
        newFN.prototype.Super = function (level) {
            var caller = arguments.callee.caller;
            var sup = this.Self().__super;
    		while(--level && sup)
    			sup = sup.__super;
    		return sup;
        };
     
    	// definition de Self
        newFN.prototype.Self = function () {
            var caller = arguments.callee.caller;
            // si le caller est "super" alors notre fonction "caller" et le caller de "super".
            if (caller == newFN.prototype.Super) 
    			caller = caller.caller; 
            var instance = null;
            // vérifie si la fonction "caller" appartient au "this" dans quel cas nous sommes dans une méthode de la dernière classe instanciée (enfant le plus bas)
            for (var i in this)
            if (this[i] == caller) instance = this;
            // sinon on parcours la récursion des objets "__super" pour trouver à quel niveau se trouve la fonction appelante(caller) et retourner l'objet "__super" correspondant.
            for (var obj = this; obj; obj = obj.__super) {
    			// pour chaque élément des objets
                for (var i in obj)
    				// si la fonction original est notre caller
    				if (obj[i].__original == caller) 
    					instance = obj;
            }
    		// on retourne la plus haute instance contenant notre méthode appelante 
            return instance;
        };
     
        newFN.__PARENT = parent;
        return newFN;
    }
     
    // --------------------------------------------
     
    var alert = function (a) {
            document.writeln(a, "<br/>");
        };
     
    // --------------------------------------------
     
    // Parent
    var Parent = function () {
            // Attributs privés
            var _maVar = null;
            // Constructeur
            this.__construct = function (val) {
                alert("__construct parent");
     
                this.setVar(val); // /!\ Normalement différent de this.self().test();
            }
            // Méthodes
            this.setVar = function (a) {
                alert("parent");
                _maVar = a;
            }
        };
     
    // Enfant
    var Enfant = function () {
            this.__construct = function (val) {
                alert("__construct enfant");
                this.Super().__construct(val); // On est obligé de passer la valeur au constructeur, si on met null et qu'on utilise le setter ici, le parent risque d'avoir le dernier mot et de mettre null dans la variable.
            }
            // Méthodes
            this.setVar = function (a) {
                alert("enfant");
                // Traitements spéciaux à l'enfant
                // Enregistrement
                this.Super().setVar(a);
            }
        };
     
    // Petit
    var Petit = function () {
            this.__construct = function (val) {
                alert("__construct petit");
     
                this.Super().__construct(val);
            }
            // Pas de méthodes
        };
     
    var Enfant = Parent.Extends(Enfant);
    var Petit = Enfant.Extends(Petit);
     
    var instance = new Petit();
    instance.__construct("super");
    edit: petit update, Super() peut prendre un paramètre(optionnel donc) indiquant de combien de parents on veut remonter, donc un entier compris entre 1 et beaucoup (1 par défaut)


    edit: exemple concret d'utilisation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    ...
    function D{
        this.test = function(){
            this.__super.__super.test(); // this étant un objet de type E, on remonte 2 fois grace à __super, on déclenche donc test() de C.
            this.Super(2).test(); // on est dans D et on remonte 2 fois depuis l'instance de D, on déclenche donc test() de B.
            this.Self().__super.__super.test(); // identique à la ligne précédente.
        };
    }
    ...
    A.Extends(B).Extends(C).Extends(D).Extends(E); // donc E enfant de D enfant de C enfant de B enfant de A.

  14. #74
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    C'est tout bon pour ma part, j'ai normalement testé quelques scénarios bien complexes et ça fonctionne sous FF et IE.

    Un merci chaleureux pour l'aide de tous, c'est vraiment niquel

    Je place le topic en "résolu", mais il reste bien évidemment ouvert si il y a encore des suggestions.
    Le code est dores et déjà en production ici (spéciale dédicace).

    Au plaisir de se recroiser sur le forum willpower

  15. #75
    Membre Expert Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Par défaut
    Sans testes je peux déjà te donner des cas qui ne fonctionneront pas :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var myClass = function(){
     
        this.subClass = function(){};
     
     
        this.__construct = function(){
            var o = new this.__super.subClass(); // error à cause du new qui retournera un objet de type s ("var s = function(){ f.apply(that,arguments); }") au lieu du type f
            var test = function(){
                this.self().foo(); // error de self non accessible depuis ube fonction imbriquée
            };
            test(); // déclenchement de la fonction imbriqué
        };
     
    }

  16. #76
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Selon moi ce cas ne devrait pas se présenter. Les classes sont toutes déclarées indépendamment dans mon idée.

    Ici, SubClass sera forcément déclarée comme Parent et Enfant.

    A moins que je n'ai pas tout compris correctement ca ne devrait pas être un problème pour moi.

+ Répondre à la discussion
Cette discussion est résolue.
Page 4 sur 4 PremièrePremière 1234

Discussions similaires

  1. Implémentation Runnable et héritage
    Par abdelilah dans le forum Débuter avec Java
    Réponses: 1
    Dernier message: 04/03/2010, 01h11
  2. Réponses: 8
    Dernier message: 10/11/2009, 21h41
  3. Héritage et d'appel de méthode
    Par Sunsawe dans le forum Langage
    Réponses: 11
    Dernier message: 30/07/2009, 23h08
  4. Réponses: 7
    Dernier message: 24/09/2008, 11h18
  5. Héritage : problème d'appel de méthodes
    Par parano dans le forum C++
    Réponses: 15
    Dernier message: 02/03/2007, 14h42

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo