Bug (?) : super, qui es-tu donc ?
Par -Alexandre LEGOUT aka LAlex- le jeudi, octobre 16 2003, 17:10 - AS2 - Lien permanent
MAJ : Aprés une étude plus approfondie du sujet avec Timothée Groleau, il apparaît que ce n'est pas vraiment un bug, mais je dirais plutôt un comportement assez particulier de l'instruction super utilisée avec apply ...
Je suis tombé sur un bug assez bizarre avec super, qui intervient lorsque l'on utilise un héritage sur plusieurs niveaux, et l'instruction apply. Une utilisation particulière provoque une récursivité infinie dans l'appel de méthodes.
Voyons tout d'abord le code :// Classe Parent
Le raisonnement est simple : la dernière classe applique la méthode doIt de sa classe mère (Child) qui elle-même applique la méthode doIt de sa classe Mère (Parent). A mon avis, la recursivité est provoqué par le fait que le super dans la méthode Child.doIt pointe encore vers Child.doIt ... :? Pourtant, si on appelle super.doIt sans utiliser apply (donc en faisant uniquement super.doIt()), la recusrsivité ne se fait pas ...
Cela voudrait dire que super est en fait lié à this ?!? 8O J'avoue que je nage complètement, alors que je ne pense pas que mon raisonnement soit mauvais. J'ai effectué des tas de tests, mais j'avoue que ja na comprend pas trés bien de quoi il en retourne.
Tout d'abord, il faut savoir que super est un objet, donc que se passe-t-il lorsque qu'on execute super() ?!? C'est bien un appel à une fonction ... sauf que super.apply(...) ne fonctionne pas...
class Parent {
function Parent() {
}
function doIt() {
trace("doIt");
}
}
// Classe Child
class Child extends Parent {
function Child() {
super();
}
function doIt() {
super.doIt.apply(this,arguments);
}
}
// Classe LittleChild
class LittleChild extends Child {
function LittleChild() {
}
function doIt() {
super.doIt.apply(this,arguments);
}
}
// Main
var tst = new LittleChild();
tst.doIt();
//256 levels of recursion were exceeded in one action list.
//This is probably an infinite loop.
Je me suis aussi demandé si super n'était pas une propriété de this, mais apparemment non. Voici quelques codes essayé dans la classe LittleChild :class LittleChild extends Child {
function Child() {
/*** Test 1 ***/
trace(typeof super); // object
trace(typeof super.apply) // Erreur à la compilation
/*** Test 2 ***/
trace(this["super"]); // undefined
/*** Test 3 ***/
trace(super.doIt == Child.prototype.doIt) // true
/*** Test 4 ***/
trace(super.__proto__ == Child.prototype) // false
trace(super.__proto__ == Parent.prototype) // true
}
}
La dernière série de tests m'emmène à la conclusion que super est une instance de la classe mère, créée à partir de this.constructor ... qui est buggué dans Flash MX 2004 (voir le post à ce sujet) Ensuite, toutes les methodes du prototype sont copiées dans l'instance ... Le tout ensuite est de savoir quel est le super de cet objet super ?!? :roll:
Bon, je pense pas avoir été trés clair, je suis moi-même complètement perdu ...
Je vais essayer de réfléchir à ca a tête froide, puis de clarifier tout ca ici ... ![]()
PS : Si quelqu'un se sent de l'essayer sur Flash MX, ca clarifierait deja le fait que cet bug soit dû à celui de constructor ... ![]()
Commentaires
Salut Lalex,
Hmm, encore un petit morceau de code bien mielleux :). Je ne suis pas sur que ce soit un bug mais plutot un comportement un peu complique de apply.
Il faut avoir a l'oeil ce que fait, apply: il execute une fonction quelconque a partir d'un object quelqconque. Pour donner une meilleur idee, on peut retourner a Flash 5. Sous Flash 5, apply pouvait etre emuler comme suit:
// apply se faisait a partir d'une function temporaire appliquee a l'objectapply = function(obj, func) {
obj.$_func = func;
obj.$_func();
delete obj.$_func;
}
// voila l'object
o = {myVar:5};
// voila la fonction
myFunc = function() {
this.myVar = 6;
}
// et on utilise apply
trace(o.myVar); // 5
apply(o, myFunc);
trace(o.myVar); // 6
Apply en MX/MX2004 fait la meme chose, alors c'est "normal" (attention les guillemets) qu'on ait une recursion infinie.
Une autre facon de le decrire: quand tu fais "super.doIt();", Flash sait qu'il execute une methode a partir de super et doit donc gerer la chaine des classes parents proprements. Comment il fait ca, je ne sais pas, mais comme tu le dis, sans utiliser apply, il n'y a pas de probleme.
Maintenant en utilisant apply, on est plus dans le contexte de la chaine des classes parents. On retrouve une methode dans la classe parent et on l'execute directement a partir de l'instance. du coup, super dans cette methode redevient le super parent (parce qu'ActionScript est dynamique) et ca boucle en recursion.
Est-ce que ca a du sens?
Timoth'
Oh merde!! La coloration de syntax a detruit mon code :?. J'avais utilise $_func pour la fonction temporaire.
Je le reecris differemment, en esperant que ca passe cette fois:
// apply se faisait a partir d'une function temporaire appliquee a l'objectapply = function(obj, func) {
obj.__func = func;
obj.__func();
delete obj.__func;
}
// voila l'object
o = {myVar:5};
// voila la fonction
myFunc = function() {
this.myVar = 6;
}
// et on utilise apply
trace(o.myVar); // 5
apply(o, myFunc);
trace(o.myVar); // 6
Et donc bien sur la conclusion de tout ca, c'est qu'il faut pas utiliser .apply ou .call pour appeler une methode de la classe mere pour passer tous les parametres en vracs avec arguments.
Il faut pas etre feignant et ecrire tous les parametres a la main :). J'ai pas teste mais je pense que c'est la meme chose en Flash MX AS1.
C'est bien quand tu connais le nombre de paramètres, mais imagine qu'on ait 3 ou 4 niveaux d'héritages, si on change les paramètres de la classe du haut, il faut changer toutes les classes ? 8O Ca va un peu à l'encontre du concept d'encapsulation ca non ? :roll:
Donc d'aprés ce que tu dis, super est donc bien lié à une instance et pas à une classe ce qui n'a rien de normal :roll:
Si super pointait tout simplement sur le prototype de la classe parent, ce serait plus simple quand même ... le tout est de savoir comment lui transmettre l'objet this lors d'un appel "classique" à une méthode (avec les parenthèses)... :?
Au vu du code que tu montres concernant l'impémentation de apply, on voit bien que le problème ne vient pas de apply, mais bien de la manière dont est géré super. Le comportement est celui auquel on peut s'attendre s'il existait un this.super ... :?
> Ca va un peu à l'encontre du concept d'encapsulation ca non ?
Completement, mais faut voir quand meme que le constructeur de la classe parent est public et avec lui les parametres qu'il attend. Ces parametres seraient connus meme pour une classe externe. C'est un des inconvenients de l'heritage, il y a quelques paragraphes a ce sujet dans "Design Patterns" par GoF. L'heritage a ses avantages mais comme generalement la classe fille connait l'implementation de la classe mere et parfois se base dessus, ca induit une grosse dependance (coupling) et ca va contre l'encapsulation. Ce n'est pas specifique a Flash mais a la POO en generale. "Design Pattern" favorise donc plutot la composition a l'heritage quand c'est possible.
> Donc d'aprés ce que tu dis, super est donc bien lié à une instance
> et pas à une classe ce qui n'a rien de normal
Hmm, oui, je pense cependant qu'il ne faut pas le voir comme ca. super est comme le keyword this dans la mesure ou il change de sens dans une fonction donnee, suivant l'endroit d'ou elle est appelee. C'etait deja comme ca sous Flash MX:
// premiere famille de classeLa sortie donne:p1 = function() {}
p1.prototype.doIt = function() {
trace("doIt in p1");
}
c1 = function() {}
c1.prototype = new p1();
c1.prototype.doIt = function() {
trace("doIt in c1");
super.doIt();
}
// deuxieme famille de classe
p2 = function() {}
p2.prototype.doIt = function() {
trace("doIt in p2");
}
c2 = function() {}
c2.prototype = new p2();
c2.prototype.doIt = function() {
trace("doIt in c2");
super.doIt();
}
// test avec la premiere famille
o1 = new c1();
o1.doIt();
trace("==="); // c1 - p1
// test avec la deuxieme famille
o2 = new c2();
o2.doIt(); // c2 - p2
trace("===");
// on "vole" la methode doIt d'une instance l'autre
o2.doIt = o1.doIt;
o2.doIt(); // trace c1 - p2
doIt in c1doIt in p1
===
doIt in c2
doIt in p2
===
doIt in c1
doIt in p2 <- c'est la la cle
Quand on a assigne une methode d'une classe a l'autre le sens de super a change dans la methode et a appele la methode dans la super classe de l'instance en cours.
> Si super pointait tout simplement sur le prototype de la classe
> parent, ce serait plus simple quand même
C'est vrai mais c'est impossible en actionscript et en ECMAScript 3. Une fonction est une methode seulement si elle est appelee a partir d'un object. Sinon c'est juste une fonction que tu peux assigner a n'importe quel object. Du coup le sens de super ne peut pas etre fixe dans une fonction et doit etre determine dynamiquement a l'execution, comme le sens de this.
En Java, on a pas ce probleme puisque tu ne peux pas ballader des methodes d'une classe a une autre comme ca et tu ne peux pas non plus appeler une methode quelconque sur un object quelconque comme le fait apply.
Je ne sais pas si on peut dire que tout ca est "normal" pour un language OO. Mais je pense qu'il faut garder a l'esprit les contraintes d'ActionScript, dans la conception meme du language. Je n'ai jamais touche a JScript.NET mais ce serait interessant de voir comment ca se passe la bas.
> Le comportement est celui auquel on peut s'attendre s'il
> existait un this.super ...
Oui c'est vrai, comme this et super changent en meme temps, c'est comme s'ils etaient lies.
En tout cas ca reste une facon de voir les choses. En ce qui me concerne, je ne vois pas ca comme un bug mais comme une contrainte du language. Le coup de constructor qu'on a trouve, ca oui c'est un bug.
Timoth'
Cela dit, tout ce que je dis est vrai (enfin j'espere) dans la mesure ou on se base sur AS1. Tu l'as deja dit dans un de tes posts, si MM avait fait un vrai compilateur pour AS2 au lieu d'un traducteur AS2->AS1, on aurait sans doute pas tous ces problemes :?.
Comme tu ne travailles qu'en AS2, je comprend ta frustration.
C'est vrai qu'ActionScript a des avantages qui créent également des inconvénients ... :roll: Mais ce qui me gène, c'est comment fait super pour fontionner lorsqu'on l'utilise sans apply ... le this est pourtant le même ! 8O
Pour moi, il me parrait complètement illogique que ces deux instructions aient des comportement différents ...
obj.method();obj.method.apply(obj);
C'est seulement du au fait que j'ai du mal à concevoir cela avec une syntaxe "class-based" ... mais c'est vrai qu'en AS1, ca parait beaucoup plus logique !
Et la on est partis pour au moins un an et demi d'attente. En fait, MX 2004 veut certainement convaincre les programmeurs Java/C# ... mais je pense que c'est pas gagné !
Je passe la journee sur ton blog aujourd'hui, mon boss va me tuer :P.
Juste une petite note en plus, this et super ne sont pas completement lies comme l'un une propriete de l'autre puisque dans une chaine de classes, super est invoque normallement de parent en parent (si apply n'est pas utilise) et donc represente une classe differente a chaque fois alors que this pointe toujours sur le meme object dans la chaine.
Sinon, dans ton code:
obj.method();les deux expressions sont completement equivalentes. Dans le code que tu avais tout la haut :), ce n'est pas ce qui se passait, tu avais:obj.method.apply(obj);
function doIt() {Comme on disait, this et super sont tous les deux determines dynamiquement mais leur relation change suivant la hauteur dans la chaine de l'heritage (je sais vraiment pas comment appeler ca :P). C'est la qu'est la difference. A la rigueur, je viens d'avoir une idee, qu'est-ce que ca donne si tu essayes ca:super.doIt.apply(this); // <- this et super ne sont pas le meme object
}
function doIt() {super.doIt.apply(super);
}
Je pense pas que ca puisse vraiment marcher mais comme l'expression est sense etre equivalente a "super.doIt()", ca vaut le coup de tenter :). J'essairai sous Flash MX tout a l'heure (je l'ai pas sous la main).
Sinon un truc qui n'a rien a voir, la famille en anglais, c'est: Parent, Child, GrandChild :D. LittleChild, ca veut qu'il est petit en age ou en taille. J'abuse a fond de faire une remarque comme ca, mais ca m'a fait sourire ;):P.
function doIt() {Didiou !!! 8O Ca marche !!!super.doIt.apply(super);
}
Mais bon, le faire marcher n'est pas tout, j'aimerais aussi comprendre un peu comment ca se fait ... en fait, j'aimerais bien comprendre comment se calcule dynamiquement super, mais la je suis en train de m'embrouiller complètement ... :?
Il reste aussi une petite question en suspend, c'est de savoir qu'est-ce qui se passe quand on fait super() ... :roll: Comment peut-on faire appel à une fonction alors que super est un objet ? 8O
Pour LittleChild, je savais bien que quelqu'un me ferait la remarque, mais je savais absolument pas comment dire ca, alors j'ai procédé à une anglisation barbare du mot français !!!
Bon, j'ai supprimé mes deux derniers commentaires, j'avais trop honte ...
Donc, pour revenir a des bases correctes, on sait que :
- super est calculé dynamiquement
- super est un objet, mais on peut l'appeler en tant que fonction (ca j'ai du mal ... :?)
- le constructeur de super a le même prototype que la classe située deux niveaux plus haut dans l'héritage?!? 8O (voir le test plus haut : Test 4)
- lorsque l'on teste super instanceof LittleChild, super instanceof Child, super instanceof Parent, les trois renvoient false
- Apres le premier super.doIt.apply, on se retrouve donc dans Child.doIt, avec super en tant que this, et on a this instanceof LittleChild qui retourne true ... donc pourquoi la considération précédente est-elle fausse ?!? 8O
Bref, je ne sais toujours pas d'ou viens ce super !!! :?
> en fait, j'aimerais bien comprendre comment se calcule
> dynamiquement super
Et oh dis! Faut savoir s'arreter hein ;)! J'deconne, comme piste, va jeter un coup d'oeil a __constructor__ (avec deux underscores de chaque cote). Utiliser cette propriete est la facon d'activer super sous Flash MX sans utiliser l'heritage par new. J'en parle dans mon article:
http://timotheegroleau.com/Flash/articles/private_static.htm
et le poste d'origine de Peter Edwards est la:
http://chattyfig.figleaf.com/ezmlm/ezmlm-cgi/1/28068
Donc clairement, super est base ou du moins utilise __constructor__. A partir de la a toi de trouver un arbre logique ou un truc comme ca pour nous sortir l'algorithme de recherche de super :). Moi j'arrete la pour aujourd'hui (fatigue le Timoth). Demain peu-etre et encore, je pense que je ferai juste quelques tests avec super et apply. Je dois avouer que sur ce coup la, le detail absolu ne me motive pas trop :P. Enfin on verra bien demain.
Pour decouvrir ce qui se passe, Peter Edwards avait utilise un editeur hexa et s'est tape une inspection des dll flash et du player. Autant dire que c'est du boulot et que faut assurer en assembleur. Bon courage si tu t'y lances.
Mais si tu decouvres tout, que va-t-il rester de la "magie" de Flash ;)?
Arf ... c'est crevant hein ?!?
Je connaissais la propriété __constructor__, je vais fouiller de ce côté la ...
Pour info, cette propriété arrive bien à contrecarer le bug de this.constructor, parce qu'elle correspond bien au constructeur de la classe ...
Pour info, il faut utiliser l'accés en tant que tableau associatif, sinon le compilo ne vous laisse pas faire. Donc, il faut se servir de this["__constructor__"] ...
D'ailleurs, la voila ma méthode clone() !
(on mélange un peu les deux posts la :?)
function clone() {return this["__constructor__"]();
}
Au fait timoth' >> je ne cherche pas à tout découvrir, mais a trouver la logique ...
aprés s'il ont développé ca comme des sagouins, je m'en moque un peu, tant que ca marche et que ca garde une certaine logique ...
Il ne faut pas oublier que passer du "prototype-based" au "class-based" fait aussi changer la logique, alors quand on doit mixer les deux, c'est assez chaud quand même ... :roll:
Merci en tout cas de tes éclairages la dessus ... je vais essayer de trouver d'autres trucs un peu zarb pour qu'on puissen en disctuer !!!
:lalex:
Aller, je suis pas encore parti et maintenant j'ai MX sous la main
> Apres le premier super.doIt.apply, on se retrouve donc dans Child.doIt,
> avec super en tant que this, et on a this instanceof LittleChild
> qui retourne true ... donc pourquoi la considération précédente
> est-elle fausse ?!?
A vu de nez, c'est le meme comportement que quand tu fais super.method():
le "this" dans "method" ne pointe pas sur super mais pointe sur l'instance
et donc logiquement "this instanceOf LittleChild" est vrai. Si le meme se
produit en utilisant "super.method.apply(super)", ca veut dire que this est
l'instance de base, auquel cas "super.method.apply(super)" est vraiment
l'equivalent de super.method() et ca c'est une super bonne nouvelle
puisqu'avec ca tu peux utiliser ton arguments array :).
je viens de faire un tests sous MX et c'est clair, en utilisant super.doIt.apply(super), les methodes sont appliquees proprements dans la chaine de l'heritage ET this pointe bien sur l'instance de base. C'est merveilleux
// grandparent
ca nous donne: EtT donc apres ca, tu peux passer l'arguments array, c'est pile poil:pp1 = function() {}
pp1.prototype.doIt = function() {
trace("doIt in pp1 - " + this.a);
// and assigning a proprety to this for the road <img src="http://common.lalex.com/themes/devblog/smilies/icon_smile.gif" alt=":)" class="smiley" />
this.b = "toto";
}
// parent
p1 = function() {}
p1.prototype = new pp1();
p1.prototype.doIt = function() {
trace("doIt in p1 - " + this.a);
super.doIt.apply(super);
}
// child
c1 = function() {}
c1.prototype = new p1();
c1.prototype.doIt = function() {
trace("doIt in c1 - " + this.a);
super.doIt.apply(super);
}
// creating an instance of c1
o1 = new c1();
// uniquely identifying this object by assigning a custom property
o1.a = "tata";
// test
o1.doIt();
// is the property there?
trace(o1.b);
// grandparent
pp1 = function() {}
pp1.prototype.doIt = function() {
trace("pp1: " + arguments);
}
// parent
p1 = function() {}
p1.prototype = new pp1();
p1.prototype.doIt = function() {
trace("p1");
super.doIt.apply(super, arguments);
}
// child
c1 = function() {}
c1.prototype = new p1();
c1.prototype.doIt = function() {
trace("c1");
super.doIt.apply(super, arguments);
}
// creating an instance of c1
o1 = new c1();
// uniquely identifying this object by assigning a custom property
o1.a = "tata";
// test
o1.doIt("salut", "Lalex", "putain", "ca", "marche");
output
J'ai une petite idee pour une implementation de clone comme dans java mais je posterai ca dans l'autre thread.
En effet, ca marche trés bien. Ce que je n'arrive pas à comprendre, c'est que quand on fait super.methode(), le this ne change pas, mais le super change c'est bien ca ? Mais la si je passe le super de mon niveau LittleChild, le this dans Child.doIt devrait etre le super de LittleChild non ?!? 8O
Ben... oui pour une methode normale... Dans les languages EcmaScript 3 (javaScript, actionscript), this represente toujours l'object a partir duquel la methode est appele.
Maintenant super DOIT casser cette regle sinon il ne pourrait jamais remonter la chaine de l'heritage de niveau en niveau.
Si super etait un object normal, le this dans methode deviendrait effectivement super pendant l'appel super.method() et on ne pourrait pas assigner de nouvelles valeurs sur this.
Donc pour moi, super est implemente nativement dans le player et n'est simplement pas un object actionscript comme les autres. Il n'obeit pas aux memes regles tout simplement. C'est une reponse un peu... heu... merdique, certe ;), mais c'est tellement evident que super ne respecte pas la regle du this que je ne le considere meme pas comme un element actionscript normal et je ne me pose pas trop de question a son sujet (je te dit, je suis un peu feignant sur ce coup la).
Ce que je peux dire aussi, c'est que "super" ne fait meme pas parti de la specification ECMAScript 3.
http://www.ecma-international.org/publications/standards/Ecma-262.htm
Il n'existe pas dans JavaScript et c'est une raison supplementaire pour que je le considere comme un hack de MM. Pour moi, vraiment, super n'est pas un object comme les autres. Remarque ca peut aussi etre un getter tres sophistique dont je ne connais pas l'implementation.
Ca m'etonnerai que ca te convienne mais si ca peux t'aider a dormir, c'est tout ce que je te souhaite :).
Arf, t'as bien compris comment je fonctionne ...
Mais je vais peut-être en rester la aussi ... j'ai ma solution, c'est deja pas mal !!!
Merci encore de t'être penché la dessus avec moi ! 
bon ok j'arrive sur le tard, mais la encore quelques remarques:
pourquoi faire
super.doIt.apply(this,arguments);
au depart ?
je pige pas la logique derriere cela ?
super est la pour faire reference dynamiquement a une class heritee,
donc:
- soit on ne l'implemente pas dans la sous-classe et au moment de l'appel de la methode doIt on utilisera la methode de la super-classe dans le contexte de la sous-classe (c'est automatique)
- soit on l'implemente dans la sous-classe et donc on override la methode doIt de la super-classe
- soit on "fusionne" et en implementant doIt dans notre sous-classe on fait reference a super.doIt() pour aussi appeler la methode de la super-classe mais dans le contexte de notre sous-classe
--> super.doIt.apply(this,arguments);
ca ne sert a rien
super.doIt( je, choisis, les , arguments)
si on ne veut pas choisir les arguments c'est donc qu'on veut passer tous les arguments, donc autant directement laisser faire la chaine d'heritage et ne PAS implementer doIt dans la sous-class
---> super.doIt.apply(super, arguments);
ca c'est encore pire !!!
si je vois un seul mec faire ca au boulot il se prends 2 baffes
lorsque l'on definit les methodes on est dans un mode schematique de construction, cad on construit la class pour l'utiliser ensuite dans une instance
le fait de faire reference a super c'est reutiliser une partie du schema d'une class heritée dans le schema de la sous-classe
mais des que l'on est dans une instance,
super.doIt.apply(super, arguments);
n'a plus du tout aucun sens!!!
ca voudrait dire que l'on veut executer une methode de l'instance sur le contexte d'une class heritee, c'est totalement illogique, a partir du moment ou on est dans l'instance notre seul contexte d'execution c'est l'instance elle-meme.
et je reviens donc sur la remarque du depart, pourquoi utiliser un super.doIt.apply(this,arguments); au depart ?
quel est l'utilité concrete ?
pour moi c'est un peu comme le probleme du getter en AS2 (blog de tweenpix) qui modifie la variable plutot que seulement la renvoyer.
A partir du moment ou ne respecte pas le role des objets c'est sur que on va forcement arriver a des choses bizarres.
et petite note sur ca:
non non et non,
AS2 a une syntaxe de classe, mais il reste prototype-based, alors que si on compare avec JScript.NET en mode managé lui c'est un vrai class-based qui utilise par defaut expando pour garder un coté dynamique.
AS2 c'est juste du vent pour le class-based, c'est l'IDE qui fait tout le boulot, il interprete une syntaxe a class pour la compiler en prototype-based, ce qui est surement bien pour les developpeurs venant de JAVA/C# mais qui est illogique en soit.
le prototype-based n'est pas le parent pauvre du class-based c'est juste un mode de pensée, de conception des objets différent, perso je dirais au plus proche des objets.
sur cela je finis avec un quote de laurent bossavit sur fr.comp.objet:
Voir par exemple les langages objet à prototypes. Il en est un très
connu, mais très peu utilisé pour construire des systèmes d'objet au sens
propre: c'est JavaScript. Parmi les langages plus "sérieux" - mais encore
moins utilisés - on peut citer Self ou Dylan.
Un petit article amusant qui démontre la structure par prototype de
JavaScript et sa supériorité à certains points de vue sur le paradigme de
classe qu'incarne Java:
http://www.crockford.com/javascript/inheritance.html
Des infos sur Self:
http://research.sun.com/self/language.html
Laurent
http://bossavit.com/
Salut Zwetan, He beh, tu es rechauffe dis donc! Merci pour les liens :).
A mon avis, il faut faire la distinction entre ce qui est propre et qui est du bon OO et ce qui marche.
Utiliser super.doIt.apply(super, arguments), ca n'a certainement pas l'air propre et c'est aussi un truc qui est illogique et qui est donc pratiquement impossible a comprendre si tu le passes a un autre developpeur, sur ca on est d'accord. Cela dit, ca marche en actionscript: comme on disait plus haut actionscript va appliquer l'appel de methode sur l'instance et pas sur super et c'est ca qu'on veut. On pourrait considerer le fait que ca marche comme un bug d'ailleurs, moi je prefere voir ca comme le comportement voulu de super en ActionScript.
C'est ce que j'ai dit a Lalex tout en haut mais comme il me l'a dit, il y a des situations ou tu ne connais pas le nombre de parametres que tu va recevoir, le constructeur de Array par exemple. Si tu veux sous classer Array avoir un premier parametre, disons le temps d'instanciation (par exemple) puis tous les parametres suivant comme dans Array pour les passer au super constructeur, tu seras emmerde si tu ne peux pas passer un nombre arbitraire d'arguments au constructeur super, et pour ca on a apply.
Sans doute qu'a la base, c'est une mauvaise idee de vouloir sous-classer Array comme ca, ou de creer une methode qui prend un nombre indetermine de parametres (mauvais OO?). Le truc c'est que le compilateur t'autorise a le faire et le language a des outil pour aider: apply, call). Alors meme si c'est crade, c'est sur que des personnes essaieront et y trouveront peut-etre une utilite. Il faut donc qu'on sache ce qui marche et ce qui ne marche pas. En l'occurence, super.doIt.apply(this), ca marche pas; super.doIt.apply(super), ca marche.
C'est bien de ca que je parlais !
Mais le problème était de lui transmettre les arguments, sans distinction du nombre d'arguments.
Evidemment, si c'est pour faire exactement la même chose que la méthode de la classe mère, ca ne sert absolument à rien de l'implémenter dans la classe fille... :roll:
La tu te bases uniquement sur l'exemple, qui ne sert qu'à illustrer mes propos ...
Quant à l'utilité de passer tous les arguments (celle pour laquelle j'en avais besoin en tout cas), elle vient de deux classes (que je donne dans ce blog d'ailleurs) : une classe Grid (qui hérite de Array), qui n'est autre qu'un tableau à deux dimensions, et une classe Matrix qui hérite de Grid, qui elle ne peut contenir que des nombres. Toutes deux ont une méthode fill, qui prend une liste d'arguments qui vont servir à remplir la grille/matrice ligne par ligne. Le seule différence est que la méthode fill vérifie bien que tous les arguments sont numériques avant d'executer super.fill ... et pour lui passer tous les arguments, il faut bien utiliser apply ... :roll:
En fait, ma logique de départ était "J'applique les instructions de la méthode de la classe mère à l'objet en cours, cad this" ... J'ai toujours un peu de mal à cerner pourquoi il faut passer super au lieu de this à apply, mais je commence à vaguement approcher cette notion.
C'est bien pour ca que je peste depuis le début sur le fait que MM se soit contenté de faire un traducteur ... :? En fait, sur cette version MX 2004, ils n'ont fait que de la cosmétique !!!
Ils ont arrangé l'IDE, mais n'ont pas touché au langage (a part quelques optimisations de performance de classe Array, etc..). Alors fournir une sytaxe "class-based" avec un langage "prototype-based", plutôt que de séduire les codeurs Java et autre, ca finit par prendre la tête plus qu'autre chose !!! :?
Pour en revenir aux arguments, ca me fait penser à une discussion avec un codeur dans un langage tout pourri, qui ne peux prendre ni des tableau, ni du XML ni un nombre non-délimité d'arguments pour une fonction :
"J'ai besoin de pouvoir passer un nombre indéfini d'arguments
- ben t'as qu'à faire une fonction qui prend 100 arguments, et tu te préoccupes pas de ceux qui sont vides
- oui, mais si j'ai besoin de 101 arguments ?
- ben tu mets 200 arguments à ta fonction, t'auras jamais besoin d'autant hein ?" ..... La j'avoue que j'ai un peu halluciné !!! 8O
Fil des commentaires de ce billet