Héritage en 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 ? :)

Les fichiers des 3 tests : test 1 - test 2 - test 3

Commentaires

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






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.