Héritage en Javascript
Par neolao le - Javascript
Quand j'ai lancé mon projet de refaire une application à la netvibes, je me suis mis à AJAX. Et donc je me suis demandé si on pouvait faire des classes en Javascript. J'ai rapidement été confronté à l'héritage.
Le plus étonnant, dans mes recherches sur l'écriture du Javascript, c'est que j'ai trouvé du code qui ne marche pas. Je n'ai pas poussé mes recherches pour savoir où ça s'appliquait (le javascript, ce n'est pas que sur les navigateurs internet) et je suis tombé simplement sur la documentation de Mozilla. Le Javascript est basé sur les prototypes.
Test 1
Voilà donc mon premier test :
// class MamanOurs var MamanOurs = function(nom){ this.nom = nom; this.cri = "RAWWWWRR !!!"; }; // Méthode rawr de MamanOurs MamanOurs.prototype.rawr = function(){ alert(this.nom + " crie : " + this.cri); }; // class BebeOurs // notez que pour appeller le constructeur parant, on utilise __proto__.constructor var BebeOurs = function(nom){ this.__proto__.constructor(nom); this.cri = "roooo ..."; }; // voilà l'héritage BebeOurs.prototype = new MamanOurs; // Instanciation var bouba = new BebeOurs("Bouba"); bouba.rawr();
Bon, c'est pas mal je trouve. Le nom a bien été enregistré et le cri a bien changé.
Test 2
Puis un autre test plus poussé :
var MamanOurs = function(nom){ this.nom = nom; this.cri = "RAWWWWRR !!!"; }; MamanOurs.prototype.rawr = function(){ alert(this.nom + " crie : " + this.cri); }; MamanOurs.prototype.faitSaCrotte = function(){ alert(this.nom + " fait sa crotte"); this.rawr(); }; var BebeOurs = function(nom){ this.__proto__.constructor(nom); this.cri = "roooo ..."; }; BebeOurs.prototype = new MamanOurs; // il y a 2 fois __proto__ parce qu'il faut remonter de 2 crans // on est dans BebeOurs.prototype et non dans BebeOurs comme au dessus dans le constructeur BebeOurs.prototype.faitSaCrotte = function(){ alert(this.nom + " va chercher sa maman"); this.__proto__.__proto__.faitSaCrotte(); }; var bouba = new BebeOurs("Bouba"); bouba.faitSaCrotte();
Et là, problème, Bouba va bien chercher sa maman, mais pour faire sa crotte, il a oublié son nom (ne me demandez pas où je vais chercher mon inspiration :) ).
Test 3
Après plusieurs logs, on voit bien que c'est un problème de scope. J'ai donc pensé à un delegate que j'ai découvert en Flash.
var delegate = function(pTarget, pFunction){ var f = function(){ return arguments.callee.func.apply(arguments.callee.target, arguments); }; f.target = pTarget; f.func = pFunction; return f; }; var MamanOurs = function(nom){ this.nom = nom; this.cri = "RAWWWWRR !!!"; }; MamanOurs.prototype.rawr = function(){ alert(this.nom + " crie : " + this.cri); }; MamanOurs.prototype.faitSaCrotte = function(){ alert(this.nom + " fait sa crotte"); this.rawr(); }; var BebeOurs = function(nom){ this.__proto__.constructor(nom); this.cri = "roooo ..."; }; BebeOurs.prototype = new MamanOurs; BebeOurs.prototype.faitSaCrotte = function(){ alert(this.nom + " va chercher sa maman"); delegate(this, this.__proto__.__proto__.faitSaCrotte)(); }; var bouba = new BebeOurs("Bouba"); bouba.faitSaCrotte();
Et là Bouba n'a pas oublié son nom pour faire sa crotte ! Elle est pas belle la vie ? :)
Commentaires
1. Le mardi, février 7 2006, 19:17 par ekameleon
Hello :)
Je parle aussi de l'héritage en Javascript dans mon article sur l'héritage en SSAS (c'est pareil)
www.ekameleon.net/blog/in...
Pour ma part je préfère ajouter une méthode extend au prototype de Function ;)
EKA+ :)
2. Le mardi, février 7 2006, 19:43 par neolao
moi aussi en pratique, j'ai une fonction pour étendre
mais sans rentrer dans les détails, j'ai voulu montrer simplement comment ca marche
3. Le mardi, février 7 2006, 19:44 par ekameleon
ok ^_^
4. Le mardi, février 7 2006, 22:01 par Thanh
Hey, sympa ce premier billet technique :)
5. Le mercredi, février 8 2006, 23:09 par liguorien
neolao a écrit : "Elle est pas belle la vie ? :)"
Elle serait plus belle si IE n'existerait pas :P L'implémentation du JavaScript d'IE (qui n'est pas vraiment du javascript en fait) ne supporte malheureusement pas le __proto__. La seule façon que j'ai trouvé pour que ça fonctionne, c'est d'invoquer le constructeur de la superclasse avec un apply()
[code]
var BebeOurs = function(nom){
MamanOurs.apply(this, [nom]);
this.cri = "roooo ...";
};
[/code]
C'est moins souple, mais ça fonctionne partout :)
6. Le mercredi, février 8 2006, 23:17 par neolao
C'est quoi IE ? lol
Faudrait que j'le marque quelque part peut-être, mais je ne pense plus supporter IE6 dans mes travaux. Seulement au boulot parce que j'y suis obligé :)
C'est vrai que j'pourrai parler de mes expériences marrantes avec IE, on verra.
7. Le samedi, février 11 2006, 02:21 par zwetan
pour le JavaScript de IE, il faut etre clair
JavaScript -> implementation de ECMAScript dans FireFox (SpiderMonkey engine)
JScript -> implementation de ECMAScript dans IE (et autre WSH etc.)
ActionScript -> implementation de ECMAScript dans Flash
bref, pour etre compatible partout il faut suivre ECMAScript et eviter d'utiliser les specificités des différentes implémentations comme par ex __proto__
et pour en revenir sur l'heritage en ECMAScript donc, ne pas oublier aussi de reassigner le constructor
cad
[code]
var BebeOurs = function( nom )
{
MamanOurs.call( this, nom ); //super( nom )
}
BebeOurs.prototype = new MamanOurs(); //overwrite le constructor
BebeOurs.prototype.constructor = BebeOurs; //ici donc reassigner le constructor
[/code]
example reel ici
live.burrrn.com/browser/E...
note qu'on doit le faire aussi meme si on herite
directement d'un objet statique (singleton)
[code]
var MamanOurs = {};
MamanOurs.maMethodeStatique = function()
{
alert( "maMethodeStatique" );
}
var BebeOurs = function( nom )
{
this.nom = nom; //un singleton n'a pas de constructor
}
BebeOurs.prototype = MamanOurs; //overwrite aussi le constructor
BebeOurs.prototype.constructor = BebeOurs; //ici donc reassigner le constructor
BebeOurs.prototype.methodePasStatique = function()
{
this.maMethodeStatique();
}
[/code]
autre exemple reel ici
live.burrrn.com/browser/E...
pour la prop (non standard) __proto__
on peut la remplacer avec constructor.prototype
cad:
avec FireFox
this.__proto__.__proto__.faitSaCrotte();
avec IE
this.constructor.prototype.constructor.prototype.faitSaCrotte();
enfin qd meme casse-gueulle a utiliser :)
et idem il faut bien faire attention de bien reassigner la prop constructor
8. Le samedi, février 11 2006, 02:47 par neolao
merci beaucoup pour l'explication
moi qui pensait que suivre ce qui était écrit sur le guide JavaScript chez Mozilla était bien !
Cette page m'a induit en erreur : developer.mozilla.org/en/...
Donc suivre simplement ECMAScript, j'ai pris le PDF ECMA-262, on va bien voir ^^
9. Le jeudi, février 16 2006, 01:13 par xuxu
Très interressant le billet.
Car pour nos futurs projets j'aurais besoin de m'y mettre, que ce soit en Javascript, PHP5, ActionScript ou autres.
10. Le dimanche, mars 18 2007, 13:52 par apoleidon
Bonjour,
Super billet en effet.
J'ai tout de même une question :
Est-il possible d'avoir des variables "protected",
que seul les classes dérivées puisent voir ?
Bien cordialement.