ActionScript3.0学习之路(2)——奇怪的变量作用域

今天发现了一个非常奇怪的现象,那就是在ActionScript3.0里面的变量作用域,已经超出了我对于C、Java、C#、Javascript等语言的所有作用域的认知,一般来说,大家认为的变量作用域的普世原则就是一个大括号,我曾经天真的以为所有的语言都应该是这样,然后我发现我错了,ActionScript3.0就不是,这个现象是怎么发现的呢?是我在写for循环的时候发现的,如下代码:

package { public class Test { public function method():void { for (var i:int = 0; i < 5; i++) { } for (var i:int = 0; i < 5; i++) { } } } }

编译居然不通过,原因是重复定义变量,我当时就纳闷了,变量的作用域不是一个大括号?试了才知道,原来真不是,实践是检验真理的唯一标准。

 

于是就出现了一个比较无语的现象,如下代码:

package { public class Test { private var j:int = 9; public function method():void { var i:int = 3; trace(j); if (i == 5) { var j:int = 1; } trace(j); } } }

 

输出多少?(注:我把类的属性j去掉也是能通得过编译的,第一个trace居然能在j被声明之前访问j)按照逻辑分析,前面那个j应该是属性j应该是输出9是吧,i等于5不可能成立,于是if代码块不可能会执行,后面那个j也应该是输出9吧,那就大错特错,后面那个j输出结果居然是0,为何?

 

我曾经怀疑过是不是ActionScript3.0也像Javascript一样,变量不用声明就可以直接使用,结果我删掉属性j,去掉局部变量j的声明,改为直接访问,无法通过编译,这说明ActionScript3.0是不支持使用未声明的变量的。

 

按照我的分析,只能这么解释,ActionScript3.0在初始化一个类的时候,把所有的属性和局部变量声明语句都先初始化为默认值,然后把所有的变量划分为是属于类的属性还是某个方法的局部变量,下次遇到局部变量声明语句时,将声明语句变为赋值语句,若在方法内遇到可能是属性也可能是局部变量的变量访问时,看其到底是在同名局部变量之前还是之后,之前就访问属性的值,之后就访问局部变量的值。

 

这样一来,以下的种种都能说得通了。

 

首先,上面第一个j是9,第二个j是0。因为i等于5不成立,已经被变为赋值语句的j的声明语句并未执行,于是j没有被赋值为1。

 

然后我们改if中的判断条件,让它成立,第二个j输出结果果然变成了1,声明语句已经被当作赋值语句用了。

 

接下来我们去掉属性j,运行并未报编译错误,说明类初始化的时候,局部变量的声明就已经执行了。这时输出结果为第一个j是0,第二个j也是0,因为if代码块未执行。

 

然后我们把if条件改为成立,这时输出结果为第一个j是0,第二个j是1,显然这里声明语句完全被当作赋值语句使用了。

 

综上所述,ActionScript3.0里面的作用域分为属性和方法(未尝试过类外面是否可以定义全部变量或者包变量之类的,也没有试过闭包,这些以后有空再试试),属性暂且不表,局部变量的声明语句在类初始化的时候就会调用,之后它的作用就变成了两个,一是变为赋值语句,按正常的流程控制执行,二是若有同名属性,区别变量的访问是在声明语句之前还是之后,之前使用属性,之后使用局部变量。

 

非常奇怪ActionScript3.0这样的设计到底是出于一个什么样的考虑,虽然一般人不会这样用,但编译器未做语法限制,一旦这样使用,会使得逻辑变得相当混乱,难以理清,清晰度远不如基于大括号的变量作用域,个人认为应该算是一个不良设计。

 

以上测试是在Flash Builder 4 PlugIn for Eclipse和Flash CS 4之中进行的(变量重复定义在Flash CS 4中是通不过编译的,除非改成“非严谨模式”,而Flash Builder 4则只是警告,不影响编译,可能Flash Builder 4默认是采用非严谨模式吧),是否编译器问题不得而知,不过这可是官方发布的工具,应该不至于吧,若以上分析有逻辑错误,也欢迎大家指正。

 

最近偶然看到一些ActionScript3.0关于反射方面的知识,发现有牛人的解答让我有些豁然开朗的感觉。ActionScript3.0是一种编译型语言,而并非解释型,最终是要编译成swf文件,编译型语言的特点是生成的文件直接可以运行,不需要依赖太多的库,用一些基本库就行了,但是用户自己写的类是不包含在基本库里面的,于是,编译器就需要把代码里面用到的所有类的信息全部包含进swf文件里面,所以就有了编译一开始就检查所有的声明语句并执行了,这样可以只把用到的类编译进swf文件,使它的体积更小,便于网络传输。

 

然后今天看了一下官方的文档,发现由于ActionScript3.0不存在块级作用域,编译器会将函数内所有局部变量 声明 提升到函数顶部,但是赋值语句却不会提升,所以才造成了以上的有趣现象,局部变量在未声明之前就可以使用。

你可能感兴趣的:(Flash)