阅读本篇前需要先阅读的教程内容:
LayaFlash引擎基本支持了全部AS3语言的API,但由于AS3与JS语言巨大的差异,仍有不到十条的LayaFlash开发规则需要开发者熟知,按照规则开发项目,或对已有的项目中不匹配的代码进行调整,因此,无论是开发新项目,还是旧项目的转换,都必须熟悉这些开发规则。
1.显示对象的使用规则
1.1 SimpleButton不支持的四个状态
和AS3的SimpleButton类不同的是LayaFlash的SimpleButton继承自MovieClip类,不支持动态设置按钮的四个状态外观,包括按钮弹起(upState)、鼠标悬停(overState)、按下(downState)、点击热区(hitState)四个状态。也因此SimpleButton允许通过addChild方法将按钮图片资源等其他显示对象添加到其中显示。教程提供的示例项目利用了这个特性在JS下显示按钮对象。
示例项目源码下载:layaFlashSimpleButton.zip
1.2 加载图片二进制数据时Loader不可用
用到Loader类的loadBytes方法加载图片的二进制数据时,需要用URLLoader类加载,因为在H5中,图片资源都使用img对象表示,而没有二进制数据可用。
2.必须通过宏编译解决差异的API
有一些API在返回结果上AS与JS无法保持一致,这部分API要想Flash版本与HTML5的版本保持一致,就需要通过宏编译的方式来解决这个差异,下面分别介绍存在差异的API:
2.1 SharedObject方法差异
AS3中获取一个未被使用过的ShareObject对象,访问它的size属性返回的值为0,LayaFlash中访问size属性返回值为2。
示例代码:
var share:SharedObject = SharedObject.getLocal("layaFlashShare"); trace("ShareObject size is: " + share.size);
另外需要注意的是,往LayaFlash的SharedObject对象中存储数据时,数据不能是自定义类的对象,且每次存储必须调用flush方法。
2.2 hasOwnProperty() 方法差异
hasOwnProperty()方法用于判断字典对象中是否包含有某个属性,由于原生Dictionary编译成h5后会存在内存泄露,进而导致游戏性能下降,为了彻底解决Dictionary带来的性能问题,LayaFlash提供了一个替代类LayaDictionary,并提供了contains()方法替代hasOwnProperty()方法。
示例代码:
var data:LayaDictionary = new LayaDictionary(); data.set("a",1); data.set("b",2); data.set("c","a string value."); data.set(4,[]); var key:Array = []; //使用一个复杂对象作为键名保存数据 data.set(key,"any data"); //输出为true trace("Is data has aListKey: " + data.contains(key));
(注意:如果代码部分有看不懂的地方,请大家参考LayaDictionary的使用规则LayaFlash新增方法)
3.XML使用规则
由于JS没有原生的XML处理类可用,所以LayaFlash根据AS3处理XML的功能最大程度支持了一个XML类,用于在JS中处理XML数据,为了兼顾性能和功能,做了一些修改,使用时需要注意一些写法上的差别。
XML示例项目源码下载:layaFlashXML.rar
3.1 定义XML对象
涉及直接使用尖括号“<>”语法定义子节点的方式定义XML时,子节点必须闭合,例如:<a />必须写成<a><a/>。以下是示例代码:
/** * 定义XML对象 * */ private function layaFlashXMLInit():void { //AS3中XML正常的初始化方式 /*[IF-FLASH]*/ var xml:XML = new XML(<a><b c="1" /></a>); //JS中定义XML,子节点标签必须闭合,不支持<a><b c="1"/></a>的写法 //[IF-JS]var xml:XML = new XML(<a><b c="1" ></b></a>); var value:int = int(xml.b.@c); trace("xml a.b.@c = " + value); }
3.2 XML节点名的命名限制
XML的节点名命名不可以使用一些LayaFlash底层已使用过的限定名,这是因为JS本身没有实现过XML的操作功能,我们在JS里所使用的XML对象是由LayaFlash模拟的XML类实现的,因而在使用的过程中会受到这个限制。这些不可用的限定名:name,node。
在开发中遇到需要定义XML内容的时候,要尽量避免使用这些限定名作为节点名。
3.3 读取XML属性值的注意事项
从XML中读取到的属性值,如果原计划要读取到的是数值类型,必须做数值类型的强制转换操作:
var value:int = int(xml.b.@c);
这里的类型强转操作是比较容易漏掉的地方,在转换编译现有项目的时候也是修改重点之一。
3.4 XML读取子节点时返回值注意事项
LayaFlash模拟的XML类在读取一个不存在的子节点时,需要先做非空判断,排除此节点不存在的情况:
/** * XML中的child方法测试 * */ private function layaFlashXMLChildTest():void { var xml:XML = <a><b>asdfa</b></a>;var v:* = xml.child("c").children(); trace("v is XMLList: " + (v is XMLList)); trace(v); }
以上示例代码在LayaFlash中的输出内容为:
如果尝试获取一个不存在的节点,AS3里返回一个不含子节点的空XMLList对象,JS会返回以一个描述此XML节点的数组内容。此外如果尝试从XML中获取一个不存在的节点:
var xml:XML = <a><b>asdfa</b></a>; trace("Get a null node: " + xml.a.b.c + ", the null node is XMLList: " + (xml.a.b.c is XMLList));
AS3中同样返回一个不含子节点的空XMLList对象,但在LayaFlash编译后的JS会因为获取一个空对象而报错。因而必须在LayaFlash编译前修改AS3代码,使用一些有可能为空的节点下的内容前添请加非空判断,通过判断以后才在逻辑中使用节点下的内容。
使用判断表到时从XML中取值时,暂时不支持使用“==”(等于)判断之外的表达式:
var xml:XML = <data> <item v="1">layaFlash</item> <item v="2">layabox</item> </data>; trace(xml.data.(v == "1"));
4.LayaFlash语法变动
4.1 函数的列表参数...rest
使用任意个数参数的方式定义的函数,须在参数后加上数组类型的声明:
private function layaResetArgsTest(...args:Array):void { //code here. }
4.2 for...each
使用“for...each”时,循环变量不能是类属性,必须用局部变量,否则编译后JS版本获取不到值。在采用LayaFlash开发时,建议不要采用for...each,使用for ...in替代。:
private var _layaForInKey:*; //作为 for each循环时的循环变量的类属性 /** * for each 语法差异测试 * */ private function layaForEachTest():void { var list:Array = [1, 2, 3, 4, {"value": "i'm ok."}]; for each (_layaForInKey in list) { trace("one item of list: " + _layaForInKey); } }
AS3的输出结果为:
one item of list: 1 one item of list: 2 one item of list: 3 one item of list: 4 one item of list: [object Object]
而LayaFlash中的输出结果却是:
所有的输出值都为空。将循环变量改用局部变量可解决该差异问题。
4.3.Number类型与整数类型变量
4.3.1.整型变量赋值
AS3代码里将一个Number类型的值赋给一个整数类型(int、uint)的变量,需要手动做取整操作,因为编译为JS代码后,JS不会对变量值自动取整:
AS3代码示例:
var numb:Number = 1.6; var ab:int = numb; trace(ab);
兼容JS需要修改为以下代码:
var numb:Number = 1.6; var ab:int = int(numb); trace(ab);
在向函数或方法传递参数时也要做类似的处理。
AS3代码示例:
var num:int = 2340923944589; transfrom(num); public function transfrom(value:uint):void { trace(value); }
兼容JS需要修改为以下代码:
var num:int = 2340923944589; transfrom(num); public function transfrom(value:uint):void { trace(uint(value)); }
4.3.2.字符串强制取整操作的差异
使用下列语句对一个字符串取整是,在AS3中得到0,JS中的结果则是“123”:
int("123_word")
4.4.for循环必须使用完整的大括号“{}”
在书写for循环时,LayaFlash不支持使用单行的循环体写法。LayaFlash编译过程中如果碰到这样的写法,会直接在代码附近添加一些错误标识,破坏代码原貌,提示错误位置。
以下是一个不支持的for循环写法示例:
var item:*; var list:Array = ["lucy","mike",2,3]; for each(var item in list) trace("item = " + item);
正确的写法示例,要补完大括号“{}”:
var item:*; var list:Array = ["lucy","mike",2,3]; for each(var item in list) { trace("item = " + item); }
4.5.使用不同修饰符定义的同名方法会根据创建时序进行覆盖
由于JS的语法中,所有的方法被定义时都是公开的(public),因而从AS3编译成JS后,如果原来存在两个方法名相同,但使用不同的修饰符(public、private、protected等)定义的方法,在JS中后定义的方法会覆盖早些定义的方法。例如以下代码示例,两个存在继承关系的类的写法在AS3中不会有问题。
父类代码:
package { public class Base { public function Base() { reset(); } protected function reset():void { trace("AS3 for ever."); } } }
子类定义了一个新的reset方法:
package { public class Child extends Base { public function Base() { reset(); } public function reset():void { trace("Hi,LayaFlash."); } } }
但在JS中,由于子类的reset方法是在“父类”的reset方法之后被定义,于是父类中的reset方法就被完全覆盖成了子类的方法。解决这个问题的方式是,按照正常的覆盖父类方法的方式定义reset方法,将子类作如下修改即可:
package { public class Child extends Base { public function Base() { reset(); } override protected function reset():void { super.reset(); trace("Hi,LayaFlash."); } } }
5.构造函数注意事项
书写构造函数的时候,所有修改父类属性的代码必须写在父类构造函数super()的调用语句之后,否则修改属性的代码无效。以下是代码示例:
作为父类A.as的代码:
package { import flash.display.Sprite; public class A extends Sprite { protected var data:String = "layabox"; public function A() { super(); } } }
作为子类B.as的代码:
package { public class B extends A { public function B() { this.data = "layaFlash";//这里的修改无效 trace("data is " + this.data); super(); this.data = "layaFlash";//有效的修改属性代码 trace("data is " + this.data); } } }
6.Embed元标签
LayaFlash支持部分Embed标签,目前支持以下两种:
[Embed(source="xxx.png")] private var TestImage:Class; [Embed(source="comp.swf",symbol="xxx")] private var TestSWF:Class;