Pour l'héritage en javascript, il est recommandé d'utiliser les prototypes. je ne les utilise pas.
Pourquoi ne pas utiliser les prototypes ? Je préfère profiter de l'étendue des possibilités 'basiques' de Javascript. C'est un langage extrêmement bien étudié, dont la 'logique' permet d'éviter l'usage de prothèse chimérique.Ces script imposent des règles simples :
- Règles a écrit:
- 1 - Ce langage intègre l'héritage non modifiable durant l'exécution (comme le C++, le java)
2 - Il existe 2 contextes pour les fonction : fonction de classe et fonction d'occurence (comme en C++ et en Java)
Note : inutile de de préoccuper de la différence des
3 - Ce langage intègre les pointeurs de fonction.
Toutes les fonctions sont placée dans une variable, ces variables se comportent comme des pointeurs de fonction, donc modifiables durant le traitement.
4 - la modification d'une variable de fonction n'affecte qu'une seule occurence d'objet.
Ces scripts sont compatibles avec les règles élémentaires du Javascript.
Après test, la vitesse d'exécution d'appel de fonction correspond aux autres mécanismes les plus rapides.
Pour reproduire l'héritage j'utilise 3 mécanismes :
A - Pour incorporer les attributs des parents dans la structure de l'objet en construction : utiliser la fonction '
call' du constructeur du parent (c'est classique)
B - Pour la reconnaissance de type : créer un "
attribut_de_type" dans chaque classe
Ensuite l'instruction 'if(typeOf..)' sera remplacée par : if(this.attribut_de_type!=null)
C - Pour les accès aux parents - cf. 'super' - nous plaçons la fonction-parent dans un
attribut 'super_'Plus précisément, placer la fonction dans un attribut qui comporte le même nom que cette fonction, ce nom est précédé de 'super_'
Exemple :
this.getName devient
this.super_getName.
Et enfin, pour conserver une rapidité d'exécution et ne pas dupliquer les fonctions, elles sont définies en static
D - définition de fonctions en static. Exemple :
MaClasse.maFonction_S = function(){}Elles sont ensuite placées dans l'objet lors de la construction.
Exemple :
this.maFonction = MaClasse.maFonction_S;Nous pouvons aisément imaginer un langage simple traduit en Javascript par un préprocesseur qui prendrait en charge la construction de ces fonctionnalités.__________________________________________________________________
Voyons comment réaliser ces fonctionnalités ?__________________________________________________________________
A - Appel du constructeur parent à l'aide de la fonction 'call'
C'est classique
Voir : le cours de programmez.com paragraphe 1.1
Mais, contrairement à Développez.com, je recommande cette construction
sans prototype, comme expliqué ci-dessus, en introduction.
B - L'attribut de classe.
C'est un attribut, donc inscrit après 'this.' qui existe uniquement dans la classe indiquée.
Exemple : pour la classe 'Animal', nous créerons : 'this.class_Animal'
Pourquoi créer cet 'attribut-de-classe' ?
Pour reconnaitre le type d'un objet. dans d'autres langages cette vérification de type peut s'écrire 'typeOf', 'instanceof'... mais dans notre méthode, nous ferons une simple égalité.
Il est impératif que cet attribut-de-classe ne soit présent que dans la classe qui lui est associée.
Voici une construction d'objet.
- Code:
-
function Animal(){
this.class_Animal = true;
}
var animal = new Animal();
if(animal.class_Animal){/*TODO*/};
Puisque nous utilisons le constructeur 'Parent.call(this)', les 'attributs_de_classe' des parent sera collectés lors du passage dans le constructeur du parent.
Voici une construction d'objet. voir test
- Code:
-
function Animal(){
this.class_Animal = true;
}
function Chien(){
Animal.call(this);
this.class_Chien = true;
}
var chien = new Chien();
if(chien.class_Animal){/*TODO*/}; // cette comparaison sera vraie
// puisque 'Chien' aura collecté 'class_Animal' lors de l'appel Animal.call(this);
Important : il est à noter que nous avons utilisé l'attribut-de-classe comme un simple flag, mais rien n'empêche de lui affecter une valeur, une fonction ou même un objet complexe.
Cela peut ouvrir des perspectives inattendues.
C - les accès aux fonctions parents, à l'aide de l'instruction 'super'
Rappel : lors de la construction d'un objet, les fonctions des parents sont écrasées par les fonctions des fils. Comment simuler l'instruction 'super' ?
Réponse : en conservant la fonction du parent
Comment ?
1 - Dans le constructeur, après avoir collecté les fonctions des parents, nous déplaçons la fonction du parent dans la variable-de-type.
Exemple : this.class_MySuper.myFonction = this.myFunctionAinsi l'attribut de cette fonction est libre et peut recevoir la fonction du fils.
2 - l'appel de la fonction super
- l'appel est en fait un 'call' pour conserver l'appelant 'this'
- l'appel de la fonction parent ne débute plus par 'super.' -> mais par la variable de class du parent qui comporte cette fonction 'class_MySuper.'
Avec une programmation de bucheron, nous préparons toutes les fonctions pour être appelé par "super".
Il suffit d'ajouter la fonction dans la variable-de-type. Exemple : this.class_Animal.toString = Animal.toString_S;
Mais vous pouvez créer une ptite routine qui construira ces fonctions super, dans ce cas vous ne construirez que les fonctions 'super' réellement requises.
Exemple : (voir test)
- Code:
-
//________ Classe parent ________
function Truc(){
this.class_Truc = []; // Initialise la variable de classe
// Prépare la fonction 'super'
this.class_Truc.toString = Truc.toString_S;
this.toString = Truc.toString_S; // La fonction parent est placé dans l'objet
}
Truc.toString_S = function(){return "Tiens un truc ! ";}
//________ Classe parent ________
function Animal(){
Truc.call(this); // Construction du parent
this.class_Animal = []; // Initialisation d la variable de classe
// Prépare la fonction 'super'
this.class_Animal.toString = Animal.toString_S;
this.toString = Animal.toString_S; // La fonction parent est placé dans l'objet
}
Animal.toString_S = function(){return this.class_Truc.toString.call(this)+"c'est un animal";}
//________ Classe héritière ________
function Chien(){
Animal.call(this); // Construction du parent
this.class_Chien = []; // Initialisation d la variable de classe
// Prépare la fonction 'super'
this.class_Chien.toString = Chien.toString_S;
this.toString = Chien.toString_S; // La fonction du fils receuille celle du Chien
}
Chien.toString_S=function(){
return this.class_Animal.toString.call(this)+", est plus précisément un chien";
}
var chien = new Chien();
alert(chien.toString()); // Affiche "c'est un animal, est plus précisément un chien"
// puisque 'Chien' aura collecté 'class_Animal' lors de l'appel Animal.call(this);
D - optimisation vitesse et espace : les fonction sont décrites en static, elle est ensuite placée dans un attribut d'occurence :
Pour avoir un comportement de type static - la fonction est écrite une seule fois pour toute la classe :
- la fonction doit être décrite hors du corp de la classe.
- elle doit être précédé du nom de la class :
MyClass.myFunction_S = function(...){...}note : ajoutons "
_S' ('S' comme static) à la fin du nom pour indiquer qu'elle est static.
Pour placer une fonction 'static' dans un attribut-d'occurence, une simple affectation (signe égal classique) suffit.
Exemple : this.myFunction = MyClass.myFunction_S;Exemple :
- Code:
-
//________ Classe parent ________
Function Animal(){
this.toString = MyClass.toString_S;
}
Animal_S.toString_S = function(){return "c'est un animal";}