AS3 - Scope variables little difference between AS2 and AS3.
Par eKameleon le dimanche, mars 23 2008, 20:29 - AS3 - Flex - Air - Lien permanent
Voici un rapide petit tutoriel pour illustrer une petite différence (bug ?) entre la portée des variables en ActionScript 3 par rapport à la même portée des variables en AS2.
Je ne vais pas entrer ici dans un rappel complet sur l'utilisation du scope des variables en ECMAScript (JS ou AS) mais je me suis rendu compte ce soir d'un petit problème de portabilité entre un bout de code AS2 vers de l'AS3. Je me suis donc amusé avec un petit test pour isoler ce problème.
Exemple avec portée de variable simple en AS2.
var a:Number = 1 ; var test:Function = function():Void { trace("> first : " + a) ; // 1 var a:Number = 2 ; trace("> last : " + a) ; // 2 } test() ; // output // > first : 1 // > last : 2
Analysons un peu l'exemple au dessus avant de passer à l'exemple en AS3 
Dans le premier test, je regarde juste la portée de la variable "a" à l'intérieur de la fonction test. Lorsque j'appelle la fonction test(), la première instruction dans la fonction cherche à récupérer la valeur d'une variable qui porte comme nom "a" ... Cette variable n'existe pas en local dans la fonction (variable déclarée avec le mot clé var dans la fonction) et il n'existe non plus aucun paramètre dans la fonction qui porte comme nom "a". Du coup le script va chercher à l'extérieur de la fonction (à l'endroit où elle est déclarée) si il existe une variable qui s'appelle "a" et forcément il trouve une adresse mémoire avec ce nom et il renvoi la valeur : 1
Jusque là tout va bien 
Ensuite je défini dans la fonction test une variable locale avec le mot clé var et du coup si j'appelle à nouveau une variable "a" la valeur trouvé par le script sera simplement celle de la variable locale trouvée : 2
A noter que si dans l'exemple précédent si j'ajoute un argument "a" dans la définition de la fonction et que je passe une valeur dans les paramètres de la fonction au moment de l'appeler j'obtiens :
var a:Number = 1 ; var test:Function = function( a:Number ):Void { trace("> first : " + a) ; // 3 var a:Number = 2 ; trace("> last : " + a) ; // 2 } test(3) ; // output // > first : 3 // > last : 2
Dans l'exemple au dessus la portée de variable se limite à un valeur trouvée dans les paramètres de la fonction, la première valeur de "a" vaut 3, la valeur passée dans la fonction au moment de l'appeler. Pas de soucis donc à ce niveau là 
Le même exemple en AS3 et un petit soucis ?
var a:Number = 1 ; var test:Function = function():void { trace("> first : " + a) ; // undefined var a:Number = 2 ; trace("> last : " + a) ; // 2 } test() ; // output // > first : undefined // > last : 2
Contrairement à l'exemple précédent en AS2, ce code AS3 n'arrive pas à récupérer la valeur de la variable située à l'extérieur de la fonction.
Par contre si je modifie légèrement le code précédent :
var a:Number = 1 ; var test:Function = function():void { trace("> first : " + a) ; // 1 // var a:Number = 2 ; trace("> last : " + a) ; // 1 } test() ; // output // > first : 1 // > last : 1
Si je désactive avec un commentaire la déclaration d'une variable locale avec le mot clé var dans la fonction, je peux donc accéder par portée enfin à la valeur de ma variable déclarée à l'extérieur de la fonction.
Conclusion
Au final, en AS3 il est impossible d'accéder à une variable par portée située à l'extérieur d'une fonction si cette variable est déclarée plus loin dans la fonction localement avec le mot clé "var". Il est vrai que ce type de cas de figure est très rare en si on utilise correctement les variables locales et avec une portée... mais je trouve tout de même beaucoup plus logique et intuitif la possibilité de récupérer la valeur de la variable externe à la fonction comme en AS2 à tout moment même si il existe un peu plus loin dans la fonction la déclaration d'une variable locale du même nom. Reste que ce que je trouve intuitif doit peut être suivre une logique qui me dépasse 
Je me suis demandé si ce comportement est voulu par Adobe ou non en AS3 par rapport à l'AS2 ?
Il m'a suffit d'un petit test en Javascript 1.7 (avec JSDB), mais aussi en Javascript 1.5 avec Flash Media Server et son SSAS pour voir que le problème venait de l'AS1/AS2 et pas de l'AS3.
Voici le code javascript de test :
geshi javascript
var a = 1 ;
var test = function()
{
trace("> first : " + a) ; // undefined
var a = 2 ;
trace("> last : " + a) ; // 2
}
test() ;
trace("---") ;
var a = 1 ;
var test = function()
{
trace("> first : " + a) ; // 1
//var a = 2 ;
trace("> last : " + a) ; // 1
}
test() ;
Faudra vraiment que je vois comment réagi Tamarin ou la M2 de ES4 sur ce genre de manipulation du scope.
Dans tous les cas Adobe semble avoir corrigé un bug existant en AS1 et AS2 et il va falloir que je cherche si je trouve pas une discussion sur le sujet au niveau des mailing list ECMAScript car je trouve ce comportement vraiment étrange.
Commentaires
Cela reste tout de même un comportement logique, je dirais même plus logique qu'en AS2. Je ne suis pas un spécialiste ActionScript loin de là . Mais je connais quand même un paquet de language et la porte des variables en AS2 est quand même parfois un peu tordue.
Je ne trouve pas que le comportement de l'AS3 soit tordu. Un variable dans un fonction a une portée locale, dès lors si tu fais pointer un même identificateur vers deux store différent, il est assez logique que le store ne change pas en cours d'exécution.
Plus précisément, si tu fais var a = 2; Tu va faire pointer l'identificateur 'a' vers un store qui contient 2. Lors de la compilation, le moteur doit faire un ensemble d'optimisation (comme détecter les variables locales), dès lors, il crée au début de la fonction un nouveau store pour stocker (dans le futur) la valeur 2 qui sera identifiée par 'a'. Dans le corps de la fonction, le store ne sera accessible qu'après a=2; avant, le store n'est pas accessible.
Je pense que le moteur AS3 doit transformer ton code comme ceci :
var a:Number = 1 ;
var test:Function = function():void
{
var a:Number;
trace("> first : " + a) ; // undefined
a = 2 ;
trace("> last : " + a) ; // 2
}
test() ;
C'est une pratique courante dans les compilateurs que de déplacer les déclarations en début de fonction, cela permet éventuellment de se rendre compte des redondances.
Je ne sais pas si on peut avoir accès à la traduction en langage noyaux du code AS3. Ce serait sans aucun doute intéressant.
Hello

Il est clair après quelque analyse sur ce comportement qu'il est tout à fait logique en AS3 ou ECMAScript de façon générale. En effet comme tu le dis le code reste beaucoup plus sécurisé et plus rapide si l'on évite un double scope dans les méthodes.
Reste avoir maintenant pourquoi l'AS1/AS2 ne faisais pas de filtrage alors qu'il semblerait que cette sécurité soit présente partout ailleurs dans les langages basés ECMA262 ed3 ?
En attendant faudrait chercher du côté des sources de Tamarin pour observer peut être les rouages des scope en ECMAScript.
EKA+
Salut,
J'ai pour installé tamarin et le compilateur asc d'adobe. On peut dès lors récuperer l'arbre syntaxique et le code (pseudo)assembleur. L'arbre syntaxique ne nous apprends rien... mis à part que le code n'est pas le même. La seule information que l'on peut tirer est que la différentiation se fait à la compilation.
En effet, si l'on lit les instructions avm+, on peut voir que dans le cas où il n'y a pas de 'var' la variable est globale, dans le second cas, la variable est locale et on met tout d'abord sur la pile un NaN pour le trace.
Hello


Oui en ES4 en principe les variables Number ou autre sont par défaut en NaN et plus en undefined si je ne m'abuse... D'ailleurs en principe en AS3 cela devrait être pareil ?
Pour le reste Tamarin fonctionne comme le Javascript je pense et ils définissent un scope global pour toute variable qui n'existe pas encore et qui est déclarée sans "var" dans une fonction ou n'importe où dans le code... Cela réagit exactement comme en JS1.5(ou SSAS pour Flash Media Server) et je trouve que ce serait pas mal d'avoir le même comportement en AS3.
Adobe en AS3 a mi de côté le mot clé "global" qui pourtant fait bien parti des specs ES4. Je pense qu'il reviendra en AS4 si tout va bien
eKA+
Au fait, dans ton second exemple, c'est void et pas Void.

Grace à ton post, j'ai pour la seconde fois compilé de l'AS3. Merci
Hello


Le second exemple, tu peux préciser exactement lequel ?
Car le second en principe est bien en AS2 (celui qui parle de l'argument dans la fonction dans le chapitre sur l'AS2). Même si il est vrai qu'il fonctionne exactement de la même manière au "void" prêt en AS3
EKA+
Au temps pour moi
Hi!
Milenia 0.8 final is getting closer
code.google.com/p/milgra/
cheers
Milan
Thanks for this good news ! I'm going to test it the next days

EKA+
great