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 :

Un pseudo héritage en javascript [Trucs & Astuces]


Sujet :

JavaScript

  1. #1
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut Un pseudo héritage en javascript
    Salut
    javascript n'est pas à proprement parler un langage full object
    Il est possible de définir des classes et des objets.
    Mais il n'existe pas de syntaxe propre à cela.

    javascript utilise des fonctions en guise de définition de classe.
    L’héritage n'est du coup pas aisé du tout.

    Voici comment y parvenir tout de même.
    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
     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
      <meta http-equiv="content-type" content="text/html; charset=windows-1250">
      <title></title>
      <script  language="javascript">
     
        function ClassA () {
          this.nom = "mon nom";
     
          this.alert = function () {
            alert('Je suis un A');
          }
        } 
     
        function ClassB() {
     
          /* héritage */
          this.superclass = ClassA;             // classe parente
          this.prototype = new this.superclass; // héritage des méthodes
          this.superclass();                    //appel du super constructeur
          delete this.superclass;              // inutile de garder la classe parente
     
          this.name = function() {
            alert(this.nom);
          }
          this.alert = function () {
            alert('Je suis un B');
            this.prototype.alert();        //appel d'une méthode parente
          }
        }
     
        a = new ClassA;
        a.alert();
        b = new ClassB;
        b.alert();
        b.name();
     
     
      </script>
      </head>
      <body>
      </body>
    </html>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    function ClassA () {...
    défini une fonction mais en javascript cela est aussi un constructeur d'objet.
    ou permet d'obtenir une instance d'un object construit avec le constructeur ClassA.

    Cet objet n'a pas à proprement parler de classe. Mais il a un constructeur on peu conceptuellement considérer ce dernier comme une classe. D’où le nom employé dans l'exemple.

    Comme dans tout langage à objet on peut définir des membres et des méthodes.
    un membre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
          this.alert = function () {
            alert('Je suis un A');
          }
        }
    une methode.

    Comme dans beaucoup de langage à objet c'est le point qui sert à référencer un membre ou une méthode.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        a = new ClassA;
        a.alert();
        a.nom;
    Reste le problème de l'héritage. il faudrait pouvoir dire qu'une classe fille est dérivée d'une classe parente. Mais les classes n'existant pas réellement en javascript il fait faire avec les constructeurs.
    C’est le but du code suivant dans le consturcteur ClassB
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        function ClassB() {
     
          /* héritage */
          this.superclass = ClassA;             // classe parente
          this.prototype = new this.superclass; // héritage des méthodes
          this.superclass();                    //appel du super constructeur
          delete this.superclass;              // inutile de garder la classe parente
    La solution proposée ici consiste à changer le prototype de la fonction. Par défaut celui-ci est Object l'objets dont tout dérive dans javascript.
    En faisant cela les instances de ClassB pouront utiliser les méthodes de leurs parents.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this.prototype = new ClassA
    Mais les membres ne sont pas dans le Constructeur de ClassB il faut donc faire appel au constructeur parent.
    On ne peut écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        function ClassB() { 
        ClassA();
        unA = new ClassA();
        this.unA=new ClassA();
        this=new ClassA();
    Car on ne veut pas appeler la fonction ClassA
    On ne veut pas de variable temporaire locale a
    On ne veut pas de membre this.a construit avec ClassA
    Et on ne veut pas devenir un simple objet ClassA

    Il faut donc appeler le superconstructeur qui est en fait Mais javascript interdit cet appel. C’est pour cette raison que l'on créé un membre this.superclass que l'on détruit ensuite;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
          this.superclass = ClassA;             // classe parente
          this.superclass();                    //appel du super constructeur
          delete this.superclass;              // inutile de garder la classe parente
    À partir de là nous avons un héritage proche de ce qui nous trouvons dans de nombreux langages.
    La surcharge fonctionne, le polymorphisme aussi.

    Manque un tout petit bout l'appel à une méthode parente.
    dans de nombreux langages.
    Pour parvenir à cela en javascript il faut utiliser la syntaxe:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
            this.prototype.alert();        //appel d'une méthode parente
    Concrètement vous pouvez continuer à créer vos classes en javascript de la façon la plus "naturelle" et pour celle ou vous voulez bénéficier d'un héritage il suffit de coller
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
          /* héritage */
          this.superclass = ClassA;             // classe parente
          this.prototype = new this.superclass; // héritage des méthodes
          this.superclass();                    //appel du super constructeur
          delete this.superclass;              // inutile de garder la classe parente
    et de changer le nom de la classe (ClassA)

    Nous n'avons pas un langage full object mais il y a un mieux.
    A+

  2. #2
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut
    J'ai modifié mon code aujourd'hui suite à une incompatibilité avec FireFox
    (__proto__ remplacé par prototype)

    il est désormais IE et FF

    A+JYT

  3. #3
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    halala, qu'il est loin le temps où je faisait joujou avec les constructeurs javascript. ^^

    Il est à noter qu'il y a un véritable héritage en JavaScript.

    Je pense que cet exemple peut le montrer :
    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
    <html>
      <head>
        <title>La classe ! 8)</title>
      </head>
      <script type="text/javascript">
        function constA () {
          this.trucA = 'foo';
        }
        function constB () {
          this.trucB = 'bar';
        }
        constB.prototype = new constA();
        var myObj = new constB();
        constA.prototype.machinA = 'truc';
     
        alert(myObj.machinA);
      </script>
      <body>
      </body>
    </html>
    Lorsque j'ajoute une propriété au prototype du constructeur A, mon objet (pourtant instancié plus tôt) hérite de cette nouvelle propriété.

    Mais ce qui n'existe pas, c'est l'héritage multiple. Cet exemple renvoi "undefined" :
    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
      <script type="text/javascript">
        function constA () {
          this.trucA = 'foo';
        }
        function constB () {
          this.trucB = 'bar';
        }
        function constC () {
          this.trucC = 'bidule';
        }
        constC.prototype = new constA();
        constC.prototype = new constB();
        var myObj = new constC();
     
        alert(myObj.trucA);
      </script>
    Lorsque je défini le constructeur C comme héritant du constructeur B, alors j'écrase les liens qu'il existait entre C et A.
    Car chaque objet est lié à son constructeur par sa "propriété" prototype qui est unique et qui ne peut donc contenir des liens que vers un seul constructeur.
    Il est toujours possible de simuler un héritage multiple un peu comme tu l'as fait fait avec ta "superclass".
    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
      <script type="text/javascript">
        function constA () {
          this.trucA = 'foo';
        }
        function constB () {
          this.trucB = 'bar';
        }
        function constC () {
          this.trucC = 'bidule';
          this.base = constB;
          this.base();
        }
        constC.prototype = new constA();
        var myObj = new constC();
     
        alert(myObj.trucB);
      </script>
    Mais on perd les propriétés d'un héritage multiple ; par exemple si on modifie le prototype du constructeur B les instances du constructeur C n'en hériteront pas. En même temps les cas où un réel héritage multiple est nécessaire sont rares.


    Ceci dit, je ne vois pas pourquoi tu as eut recours à un membre superclass, ça ne fait qu'enlever les propriétés d'héritage que l'on avait.
    Il faut donc appeler le superconstructeur qui est en fait
    Pourquoi veux-tu appeller this.prototype comme une fonction alors qu'il contient (pour simplifier) une instance de classA ?


    À propos des __proto__ et du parent::maMethod(); ; je pense que le premier est la réponse au deuxième. ^^

    À savoir que le __proto__ permet de remonter un et un seul niveau d'héritage. Le membre prototype est donc en quelque sorte le résultat de l'enchainement (avec écrasement des propriétés ; ce que l'on appel surcharge dans ce cas) des membres __proto__.

    Un petit exemple pour illustrer cela :
    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
      <script type="text/javascript">
        function constA () {
          this.trucA = 'foo';
          this.alert = function () {
            alert('Je suis A');
          }
        }
        function constB () {
          this.trucB = 'bar';
          this.alert = function () {
            alert('Je suis B');
          }
        }
        function constC () {
          this.trucC = 'bidule';
          this.alert = function() {
            alert('Je suis C');
            this.__proto__.alert();
            this.__proto__.__proto__.alert();
          };
        }
        constB.prototype = new constA();
        constC.prototype = new constB();
        var myObj = new constC();
     
        myObj.alert();
      </script>
    Je commence par définir le constructeur B comme dérivant du A et le C dérivant du B. Ensuite lors de l'instanciation du constructeur C, les méthodes alert sont successivement surchargés. Mais lorsque j'appel la méthode alert de mon instance celle-ci appelle les méthodes alert du constructeur parent, mais aussi celle du grand-parent.
    Si dans le constructeur C j'avais mis this.prototype.alert(); j'aurais appellé la méthode alert du constructeur B ; et ce parceque j'ai défini : constC.prototype = new constB(); .


    NB : this.prototype = new constB(); à l'interieur du constructeur C ne produit pas l'effet désiré, en fait ça ne fait rien. Car en fait le this se réfère déjà à la future instance, et j'ignore encore pourquoi les changements ne persistent pas. Mettre this.constructor.prototype dans le constructeur C devrait normalement faire la même chose que de mettre constC.prototype à l'exterpeur des constructeurs mais j'ignore encore une fois pourquoi les changements ne persistent pas.

    Si jamais quelqu'un à envie de se pencher sur la question, sa réflexion sera la bienvenue.
    Pour l'heure je crois avoir assez embrouillé tout le monde.
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  4. #4
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut
    J'ai posté mes recherche en l'état pendant que je bricolais

    comme j'utilisais __proto__ he ne pouvais pas faire this.__proto__() d'où le superclass

    Merci de tes remarques ça va m'aider à y voir plus clair
    A+JYT

  5. #5
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    Bien que très interressant, on ne trouve que peu de documentation sur ce coté "très objet" de javascript.
    Je pense que la mailleur doc à ce sujet reste la documentation de référence du JavaScript de Netscape. Celle-ci est téléchargeable ici http://web.developpez.com/tutoriel/javascript/
    Je te conseil de lire le chapitre 7 du "Client Guide JavaScript 1.3" là dedans tu as toute le coté orienté objet qui y est décrit.
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  6. #6
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut
    merci

  7. #7
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut
    Citation Envoyé par Celelibi
    ...
    Ceci dit, je ne vois pas pourquoi tu as eut recours à un membre superclass, ça ne fait qu'enlever les propriétés d'héritage que l'on avait.
    Il faut donc appeler le superconstructeur qui est en fait
    Pourquoi veux-tu appeller this.prototype comme une fonction alors qu'il contient (pour simplifier) une instance de classA ?...
    en fait l'affectation du prototype n'appelle pas le constructeur tu obtien bien les péthodes de la classe parente mais pas les attributs.
    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
      <script type="text/javascript" language="JavaScript">
    function ClassA () {
    	this.a = "a";
    }
     
    function ClassB () {
    	this.b = "b";
    	this.prototype = new ClassA();
     
    	this.alert = function () {
    		alert(this.a + " " + this.b);
    	}
    }
     
    function ClassC () {
    	this.c = "c";
     
    	this.superclass = ClassA;
    	this.prototype = new this.superclass();
    	this.superclass();
     
    	this.alert = function () {
    		alert(this.a + " " + this.c);
    	}
    }
     
    function ClassD () {
    	this.d = "d";
     
    	this.prototype = new ClassA();
    	this.prototype();
     
    	this.alert = function () {
    		alert(this.a + " " + this.d);
    	}
    }
     
    b = new ClassB ();
    b.alert();
     
    c = new ClassC ();
    c.alert();
     
    d = new ClassD ();
    d.alert();
      </script>
    dans le premier cas this.a est undefine
    dans le deuxième tout est Ok
    dans le troisième tu as une erreur sur l'appel de this.prototype() car ce n'est pas une fonction.

    A+JYT

  8. #8
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    Dans le premier cas je ne comprend pas pourquoi les propriétés ne sont pas hérités, surtout si les méthodes le sont puisqu'en javascript un méthode n'est rien d'autre qu'une propriété qui contient une fonction. D'ailleur chez moi sous mozilla les méthodes ne sont pas hérités avec ton premier exemple si je rajoute une méthode à ClassA.


    Et pour ton dernier exemple, je me demandais juste pourquoi vouloir appeller prototype comme une fonction.
    Te viendrait-il à l'esprit de faire un code du genre :
    var truc = new Array();
    truc();
    Et bien remplace "var truc" par "this.prototype" et "Array" par "ClassA".


    Dans ton deuxième exemple tout est OK, sauf que si tu modifie ta ClassA les changements ne seront pas hérités.
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  9. #9
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut
    Un retour sur cette vieille discus pour ajouter quelque précision et un nouveau bricolage.

    dans l'exemple de départ je cherchais à faire de l'héritage et obtenir un moyen de definir mes classes d'une façon plus conventionnelle.

    j'ai utilisé la notion d'objet au travers de fonctions qui étaient vues alors commme constructeur.

    Celelibi m'a effectivemùent donné une aproche qui marchait dans sont exemple mais pas comme je l'entendais. d'où une certaine incompréhention.

    en fait après coup et dévelomment plus sérieur j'en suis venus à reprendre tout ça et j'ai compris ce qui nous séparait.

    Celelibi me propose de faire de l'héritage sur les objets alors que moi je cherche à le faire au niveau de la classe.

    bref dans mon esprit je cherchais à faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class toto extend titi {
      method test() {
        alert('test');
      }
    }
     
    myObj = new Test();
    en non pas
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    toto = new class();
    toto.extend = titi;
    toto.test = method() {
      alert('test');
    }
    du coup l'héritage étant inclus dans le constructeur ça ne fonctionnait pas comme me le proposait Celelibi.

    finalement je me suis fait une fonction Class qui me retourne une classe
    elle prend 1 ou 2 arguments
    le body ou la classe parent et le body ce qui donne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    classA = Class ({
      name: 'truc',
      alert: function () {
        alert(this.name);
      },
      initilize: function () {
        alert('init classA object');
      }
    });
    ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    classB = Class (classA, {
      prenom: 'chose',
      alert: function () {
        alert(this.name + ' ' + this.prenom);
      },
      initilize: function () {
        alert('init classB object');
      }
    });
    l'usage est on ne peut plus classique en POO
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var A = new classA();
    var B = new classB();
     
    A.alert();
    B.alert();
    pour cela il faut inclure la définition de la fonction CLass qui construit la classe
    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
    Class = function (extend, body) {
       theClass = function () {
     
       if ((typeof(body) == 'undefined') && (typeof(extend) == 'object')) {
          for (property in extend) {
           this[property] = extend[property];
          }
       } else if (typeof(body) == 'object') {
          if(typeof(extend) == 'function') {
             this.superclass = extend;
             this.prototype = extend;
             this.superclass();
             delete this.superclass;
          }
          for (property in body) {
           this[property] = body[property];
          }
       } 
     
       if (typeof this.initilize == 'function')
          this.initilize();
       };
       return theClass;
    }
    A+JYT

Discussions similaires

  1. Héritage en javascript
    Par Attrox dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 27/09/2014, 14h57
  2. Héritage en JavaScript ?!
    Par totof974 dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 25/08/2014, 18h23
  3. Héritage en JavaScript
    Par scandinave dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 06/02/2013, 19h05
  4. Balise OBJECT et héritage (css/javascript)
    Par Dionyzos dans le forum Balisage (X)HTML et validation W3C
    Réponses: 5
    Dernier message: 01/04/2007, 11h08

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