Fonctions globales et ciblage
Par -Alexandre LEGOUT aka LAlex- le mardi, octobre 12 2004, 17:25 - AS2 - Lien permanent
Depuis quelques temps, j'ai décidé de ne plus utiliser le this pour le ciblage de mes méthodes et propriétés à l'intérieur d'une classe. Ca paraît plutôt logique, et c'est une pratique tout à fait répandue dans d'autres langages OO. Seulement, lors du codage de mes classe d'intervalles, je me suis rendu compte que le ciblage posait quelques problèmes sur des noms de méhodes globales, renseignées dans le fichier toplevel.as ... C'est d'ailleurs pour cela qu'on retrouve des this.stop() dans ces classes.
En effet, si ma classe contient une méthode stop(), et que je l'appelle sans le this, il semble que ce soit la fonction globale qui soit appellée (en tout cas, pas celle de la classe) ... Pourtant, si je compare stop (sans ciblage) à this.stop, il me retourne bien une égalité ! 8O. Voyez plutôt :class TestStop {
function TestStop() {
trace("TestStop instance created");
trace("--------------------");
}
function stop() {
trace("stop method called");
}
function testScope() {
stop();
trace("Global stop ? " + (stop == _global.stop));
trace("--------------------");
stop.call();
trace("Class' stop ? " + (stop == this.stop));
}
}
/** Code de test */
var tst:TestStop = new TestStop();
tst.testScope();
/* Sortie
************
TestStop instance created
--------------------
Global stop ? false
--------------------
stop method called
Class' stop ? true
*/
Bizarre non ? :o
Commentaires
Flash je l'aime pour Ca :p
En poussant un peu plus loin, il semblerait que _global.stop soit undefined ... Le seul moyen d'accéder à la fonction globales de toplevel.as serait donc de ne mettre aucun ciblage. Ce qui explique le conflit avec un this implicite ...
Eh ben, encore un choix assez incompréhensible de la part de MM, surtout qu'un stop() non ciblé n'a absolument aucun sens !?! :o Car on considère souvent que le stop() tout seul cible implicitement le clip en cours si on est sur la timeline, dans un classe, quel clip est donc ciblé ? _root ? Le clip qui a instancié la classe ? Mystère ....
Faut pas oublier que les classes ne sont pas attachée en _global mais plutot dans un clip qui se nomme _packages ... c'est d'ailleurs ce qui permet d'utiliser ce clip pour créer dynamiquement un MC qui hérite d'une classe sans symbole dans la biblio ... maintenant je viens de tester pour play ton exemple et c'est la même chose
par contre c'est étrange tout de même cette limitation je m'en étais jamais rendu compte avant et c'est pas faute d'avoir utilisé ce type de déclaration de nom de méthode dans mes classes.
Vraiment étrange comme bug
bye
Donc logiquement le stop() ou play() serait ciblé sur _packages ?!
@eka > __Packages n'a pas grand chose à voir la dedans : c'est un espace ou sont contenues les classes linkées à un clip. Cela permet de faire un attachMovie d'une classe héritant de MovieClip, sans avoir besoin d'un symbole dans la librairie ...
++ ^^
Non tu as raison ... sont bien définis en global :
Avec Flare :
movie 'C:\DOCUME~1\ALCARA~1\Bureau\works\TESTDE~1\test.swf' {// flash 7, total frames: 1, frame rate: 24 fps, 740x400 px, compressed
frame 1 {
var tst = new TestStop();
tst.testScope();
trace(_global.__Packages.tst);
}
movieClip 1 __Packages.TestStop {
#initclip
if (!_global.TestStop) {
var v1 = function () {
trace('TestStop instance created');
trace('--------------------');
};
_global.TestStop = v1;
var v2 = v1.prototype;
v2.play = function () {
trace('play method called');
};
v2.testScope = function () {
stop();
this.play();
trace('Global stop ? ' + (this.stop == _global.stop));
trace('--------------------');
this.stop.call();
trace('Class\' stop ? ' + (this.stop == this.stop));
};
v2.stop = function () {
trace('stop method called');
};
ASSetPropFlags(_global.TestStop.prototype, null, 1);
}
#endinitclip
}
}
Mais malgré tout elles sont récupérables sur un clip ... c'est là que je comprends pas
Je ne suis pas sûr, mais ça m'a tout l'air de venir du compilo. Faut tester une décompilation, voir si le compilateur ne transforme pas le "stop" en l'appel d'une ancienne fonction de flash3/4.
Que ce soit dans un clip, en AS1 ou en AS2, les fonctions globales du type stop, play, gotoAndStop, duplicateMovieClip etc sont toujours prioritaires sur les méthodes de clip. C'est comme ca faut faire avec, il est donc obligatoire d'utiliser this pour différencier les méthodes des fonctions globales, et l'AS2 qui est une surcouche goret de l'AS1 ne change rien.
Le gros problème avec la non utilisation de this dans les classes AS2 (qui est sympa c'est vrai) est qu'on ne peut proprement l'utiliser, il y a toujours des cas ou il faut l'employer, utiliser une référence à this quand on veut le passer en paramètre, utiliser this quand on doit faire un stop et plein d'autre cas. Ca pose un problème c'est qu'on arrive a une situation ou la non utilisation de this devrait clarifier et simplifier le code et qu'au contraire ca embrouille et rend moins lisible l'ensemble.
En plus quand on utilise a coté de ca de l'ActionScript externe aux classes on doit bien utiliser this.
Ca me fait vraiment hésiter, entre une solution cohérente et sytématique (un peu plus lourde) et une pseudo simplification ambigue.
Théoriquement, l'utilisation du this n'est pas nécéssaire non plus en AS1, le this étant normalement implicitement en premier dans la chaines des scopes. Il s'agit donc bien d'un "telescopage" des scopes ...
En AS1, sur la timeline, le problème est strictement identique :
function stop() {trace("Stop called");
}
trace("stop()");
stop();
trace("----------------");
trace("stop.call()");
stop.call();
/* Sortie
stop()
----------------
stop.call()
Stop called */
++ ^^
this pas nécessaire en AS1 ?
monClip.onEnterFrame=function () {
stop();
}
ca donne pas tout a fait pareil, idem si la méthode est définie dans le prototype, en AS1 l'utilisation de this est une définition de chemin relatif explicite, ne rien mettre c'est un chemin implicite qui prend le scénario sur lequel la méthode est définie comme chemin des fonctions ou propriétés.
Peux-tu m'expliquer ce que tu cherches a démontrer avec ton exemple ?
Autant pour moi, je pensais que le this était systématiquement implicite ...

Ca fait longtemps que j'ai plus touché à l'AS1 !
Je le répète, vérifiez, avant de parler de scope et tout ça, que ce n'est pas simplement le compilateur qui foire : lui ne vérifiant pas le scope, il remplace simplement "stop()" par ActionStop (0x07) plutôt que par ActionCallMethod (0x52) :
.fla (mx) avec une frame :
this.stop();stop();
[flasm]
frame 0
push 0.0, 'this'
getVariable
push 'stop'
callMethod
pop
stop // <=== appel direct de ActionStop au lieu de ActionCallMethod
end // of frame 0
end
[/flasm]
Donc c'est une faiblesse du compilateur MX, et il y a des chances que ce soit pareil avec le MX 2004.
Que ce soit le langage ou le compilo, il en résulte bien un problème de scope ... aprés, à qui la faute, ca fera pas vraiment avancer le schmilblik !
Pour revenir à ce que dit Mama :
Oui, le this est nécessaire, sinon je crois qu'on se retrouve au niveau de l'objet d'activation (il faut vérifier que l'object d'activation, comme l'entend timothee groleau est bien ce qui rentre en jeu dans ce cas).
Et de ce fait, le compilateur se agit plutôt bien : puisque le comportement avec this est sans this n'est pas le même, il est logique qu'il n'essaie pas de changer les choses (en ajoutant le this si la méthode existe). Maintenant, pour le stop et ses amis, ce sont des cas particuliers : vieilles fonctions pour compatibilité ascendante, donc bytecode (pas orienté objet) et portée particulèrs... Cependant, même si le compilateur aurait pu permettre une gestion de ce problème, ça empêche simplement d'appeler une fonction "stop" appartenant à l'objet d'activation (comme je l'entends plus haut).
cf :
à propos de la chaîne de portée et de l'objet d'activation
flasm
Je me suis trompé dans mon avant-dernier post en omettant (étais-je seulement au courant ?) le scope particulier du player.
Mais, quel est au juste le schmilblik ? Un oeuf d'autruche ?
Le schmilblik, c'est comment dirais-je ?
Revise tes classiques de Coluche
Complexe, le ciblage me semble-t'il... pensait pas que c'était si comme ça
sans le this ce n'est pas l'objet d'activation qui est considéré implicitement mais bien le scénario sur lequel l'objet est posé.
...sur lequel le code est ecrit pardon
Ca dépend de l'endroit où est créée ta fonction.
En fait, je pensais que les actions de frame possédaient un objet d'activation au même titre que les fonctions. Apparemment non, et c'est directement le movie clip qui sert d'objet d'activation pour les fonctions définies sur son scénario. Ce que je voulais pointer du doigt :
Chaînes de portée à l'intérieur de f :
- fonction f définie sur le scénario d'un movie clip mc :
... <- mc <- objet d'activation de f
- fonction f définie dans fonction F :
... <- object d'activation de F <- objet d'activation de f
Donc je crois que, dans f, le "this" et le "sans this" pointent au même endroit dans le cas où l'objet d'activation de F ou le movie clip mc se voient appliquer la méthode f.
Je remets cette url :
[url:8a3d6befc5]http://www.timotheegroleau.com/Flash/articles/scope_chain.htm[/url]
Une petite astuce pour lister les arguments avec leur nom :
[as]_global.traceArgs = function() {
for (var i in this) {
if (this[i] != arguments.callee && i != "arguments") {
trace(i + " : " + this[i]);
}
}
}
this.test = function(a, b) {
var traceArgs = traceArgs;
traceArgs();
}
this.test("this is a", "this is b");[/as]
il y a 3 scopes qui sont cherchés dans un ordre précis:
1. le scope de l'instance
2. le scope qui contient l'instance (toujours un MovieClip)
3. le _global
(sans parler de chaque scope contenant sa propre chaine de prototype :P)
_global.toto = function()enlevez les comments du MC, test de compile, puis enlevez le comment de la function etc... (notez bien que il n'y pas de this devant l'appel de toto) maintenant faite pareil mais appelez toto() avec le this devant:{
trace( "_global.toto()" );
}
/*
MovieClip.prototype.toto = function()
{
trace( "MovieClip.toto()" );
}
*/
/*
toto = function()
{
trace( "toto()" );
}
*/
///---------------------
toto();
this.toto();1. la methode definit en _global ne sera jamais appelée, car le this fait pointer explicitement sur le scope d'execution de l'instance, on est donc en "local", logique que ca aille pas voir en _global.
2. si on de-commente tout, la function toto prendra le pas sur la methode toto du MC, logique.
3. si on laisse commenté la function toto, alors la methode definit dans le MC s'execute, la encore logique.
(s'amuser avec __resolve ca aide bcp :))
sauf que dans le cas d'une methode comme "stop",
on se trouve dans un cas particulier: stop est avant tout une action, pas une fonction
comme le précise m-e ca ne compile pas pareil
la methode stop existe que depuis flash5
l'action stop elle depuis flash2
bref, stop() utilisé sans le this n'est pas a considérer comme une méthode,
une action prends toujours le pas sur une methode et/ou fonction.
Et même avec AS2, tout comme en Java, le "this" a donc son utilité.
(sans parler des bugs du goret de AS2)
J'ai exactement les mêmes apprehensions que mama à me passer du this que ce soit en AS2 ou en AS1. Je trouve même pas le code beaucoup plus lisible sans, qu'avec, mais bon, c'est une question d'habitude sûrement.
c'est bien le this quelle drole d'idée de vouloir l'enlever !!!?
Fil des commentaires de ce billet