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"); |