9. 因此,几乎每个 MXML 组件的后面均存在一个 ActionScript 3 类(实际上,有些 MXML 标记没
有相应的 ActionScript 类,例如 Script 和 Model)。在下面的范例中,这是一个源自 Label 类的
一个代码片段( snippet ):
1: public class Label extends UIComponent
2: implements IDataRenderer, IDropInListItemRenderer,
3: IListItemRenderer, IFontContextComponent
4:
5: {
6: /**
7: * Constructor.
8: */
9: public function Label()
10: {
11: super();
12:
13: // this is so the UITextField we contain can be read by a screen-reader
14: tabChildren = true;
15: }
16:
17: /**
18: * @private
19: * Flag that will block default data/listData behavior.
20: */
21: private var textSet:Boolean;
22:
23: ...
24: }
In any Flex application you have at least one MXML file, which is the main application. For example,
here is the complete code for the “Hello World!” application:
18. 复杂数据类型:对象( Object)、数组(Array)、矢量(Vector )(以 Flash Player 10
开头)、字典(Dictionary)、位图(Bitmap)、字节数组(ByteArray)、日期
(Date)、XML、XMLList、函数(Function)、错误(Error)和 RegExp 等。
在 AS3 语言中,一个变量就是与实际值关联的一个标识符或指针。AS3 中的合法值可以是对象
(int 或 uint 是对象,Number 或 Date 也是对象)、null 和 undefined。null 和 undefined 均表示数
据缺失,但它们之间存在差别。当你声明一个变量并且你没有对其初始化时,并且如果变量的
类型不是 Boolean、int、uint 或 Number,则该变量具有值 null 。如果变量没有赋予数据类型并
且没有初始化,则该变量具有值 undefined。同时,当你拥有一个动态对象并且希望检查一个特
定的方法或特性(property)是否被定义时,你可以对 undefined 进行检查。
在 PHP 中,你可以按照下面的方式声明一个变量:
1: $anInteger = 12;
2: $isTrue = true;
3: $aString = "my string";
4: //or
5: $aString = 'my string';
在 AS3 中,你可以使用 var 关键词声明一个变量:
1: var anInteger:int = 12;
2: var isTrue:Boolean = true;
3: var aString:String = "my string";
4: //In AS3 you can not use simple quotes for declaring a String
注意在该变量名称之后,我添加一个类型注解;例如,myVarName:Boolean (该类型是使用 “:”
声明的,后面紧跟相应的类型)。在 AS3 中,你可以使用类型注解,也可以不使用类型注解
(如果编译器设置为严格模式(strict mode ),则你必须使用类型注解)。
对于 PHP 来说,你不必声明变量的类型,因此,这一点也许显得奇怪,而且你可能希望坚持使
用不指定类型的方式编写程序。由于使用类型注解具有诱人的优点,我强烈推荐你使用类型注
解。首先,当你使用 IDE 编写代码时,对变量进行分类将使得你在编译时发现更多错误。例
如,设想具有一个单一 String 自变量的函数。如果你试图调用该函数,将一个 Object 作为自变
量进行传递,则 IDE 将提醒你这是错误。
如果不使用类型注解,你可能会遇到运行时错误,但只能在你或最终用户运行该应用程序时才
会发现,并且此时查明软件缺陷的根源将会非常困难。
坚持使用类型注解的第二个原因是,如果 AS3 编译器知道变量的特定类型,则其能够对应用程
序进行优化。
19. 在 PHP 中,你能够利用每次赋值改变变量的类型:
1: $myVar = 12; //it is an int
2: $myVar = "now is a string";
在 AS3 中,只有当你使用“*”声明变量为 untyped 时,你才能够完成相同当操作(使用严格模
式)。
1: var myVar:int = 12;
2: //this will raise an error and the application can not be compiled
3: myVar = "this is a string";
4:
5: //declaring the variable untyped you can change the type with each assignment
6: var myVar2:* = 12;
7: myVar2 = "this is a string now";
你可能注意到只有当声明变量时我才使用关键词 var。对于下一步赋值操作,你可以忽略 var 和
类型注解。
正如我前面所述的那样,在 AS3 中变量仅仅是实际值的指针。然而,当你将 int、uint、
Number、Boolean 或 String 变量赋值给另一个变量时,将会创建一个副本(对于将这些类型的
一个变量传递给一个函数时,情况也是相同的)。在 PHP 中,你可以使用 “&” 运算符通过引用
为原语类型进行变量赋值;当你为一个变量改变值时,其它变量将指向相同的改变值。
在 PHP 中,你使用“.” (dot)对字符串进行串接,而在 AS3 中,你可以使用“+” (plus)完成相同的
任务:
1: //in PHP
2: $space = " ";
3: $a = "this" . $space . "is!";
4:
5: //in AS3
6: var space:String = " ";
7: var a:String = "this" + space + "is!";
在 PHP 中,你可以在任何你期望的位置定义变量:例如在文件级、在函数中或在类中。在 Flex
应用程序中,变量只能在函数中或类级别中声明。
30. 化提供了机会,因此子类将不会使用没有设置的成员。你可以使用语句 super()调用父构造器。
让我们来看看这些工作原理,先看 PHP 代码,然后再看 AS3 代码。
1: class SimpleClass {
2:
3: function SimpleClass() {
4: echo('SimpleClass() called');
5: }
6:
7: function __construct() {
8:
9: }
10:
11:
12: function displayVar()
13: {
14: echo "SimpleClass classn";
15: }
16: }
17:
18: class ExtendClass extends SimpleClass {
19:
20: function ExtendClass() {
21: $myVar = 1;
22: parent::SimpleClass();
23: //or
24: parent::__construct();
25: }
26: // Redefine the parent method
27: function displayVar()
31. 28: {
29: echo "Extending classn";
30: parent::displayVar();
31: }
32: }
1: public class SimpleClass {
2:
3: function SimpleClass() {
4: trace("SimpleClass() called");
5: }
6:
7: public function displayVar():void
8: {
9: trace("SimpleClass class");
10: }
11: }
12:
13: public class ExtendClass extends SimpleClass {
14:
15: function ExtendClass() {
16: super(); //we have to call first the parent constructor, and only after we can
execute our code
17: var myVar:int = 1;
18: }
19:
20: override public function displayVar():void {
21: trace("overrided displayVar()");
22: super.displayVar();
23: }
32. 24: }
让我们看一下在 AS3 中类是如何初始化的。当一个类被例示时,首先其所有的特性
(property)将被初始化,其次在类级别定义的静态代码将被执行(这在 PHP 中是不能执行
的),最后构造器将被执行。下面是范例代码:
1: public class Foo {
2:
3: private var a:int = 0;
4: private static var os:String;
5: trace("initializer");
6:
7: if (Capabilities.os == "LINUX")
8: os = "LINUX";
9: else
10: os = "other";
11:
12: public function Foo(a:int=1) {
13: trace("foo() executed");
14: }
15: }
16:
17: var foo1:Foo = new Foo();
18: var foo2:Foo = new Foo();
19: //produces this output in console:
20: initializer
21: foo() executed
22: foo() executed
在 AS3 中,你可以使用函数的原型特性(property)创建函数闭包之外的对象(这与你在
JavaScript 中使用的函数来创建/扩展类的情况是相似的)。下面是一个简短范例代码:
1: //we create a function
2: function MyClass(value:String = "Mihai") {
33. 3: //we create a property
4: this.name = value;
5: }
6: //we use the special variable prototype of the function
7: //to create another method
8: MyClass.prototype.setName = function (value:String):void {
9: //we have access to the property defined on MyClass object
10: trace(this.name);
11: this.name = value;
12: trace(this.name);
13: }
14:
15: //create an instance
16: var myObject = new MyClass();
17: //accesing the method created earlier
18: myObject.setName("Joe");
在后面的章节中,我将更多地讨论 AS3 的动态功能。
Getters/setters
在任何 OOP 语言中,你通常会使用 getters/setters 来控制你希望向外部暴露的类特性
(property)。PHP 也不例外。然而,在 AS3 中,可以使用关键词 set 和 get 对类特性
(property)提供特别支持。下面是范例代码:
1: public class Employee {
2:
3: private var _salary:int = 0;
4: private var _income:int = 0;
5:
6: function Employee() {
7:
8: }
34. 9:
10: public function set salary(value:int):void {
11: if (value > 0) {
12: this._salary = value;
13: this._income = this._salary * 12;
14: }
15: }
16:
17: public function get salary():int {
18: return this._salary;
19: }
20:
21: public function get income():int {
22: return this.income;
23: }
24: }
25:
26: //using this class
27: var emp:Employee = new Employee();
28: emp.salary = 1000;
29: trace(emp.income);
30: //this raise an error, because the income property is read-only
31: //for the outside code
32: emp.income = 120;
通常,尽管我使用 setter 和 getter 替代_salary 字段 ,但我可以调用这些方法,好像它们就是字
段或特性(property)而不是函数:以 object.salary = 20 替代 object.salary(20)。如果你选择不对
setter 进行定义,你可以获得只读特性(property)。这就是我使用_income 特性(property)所
实现的功能。
除了使得代码简洁一些之外,该功能还使得编写可以被其它应用程序使用的 API 或类更为简
单。假设在我的范例中,我选择创建_salary 字段作为一个 public 成员。如果后来我决定需要验
35. 证能够设置的值,则我必须添加一个 setter。在 PHP 中,这可能需要使用如 myObject.setSalary()
的语句。此时,任何使用该类的代码将会被破坏;因此,代码必须使用 setter 进行更新。
在 AS3 中,你可以利用定义为 public var salary:int 的特性(property)来启动该类,并且当你决
定需要一个 setter 时,你可以对该变量重新命名,然后添加 public function set salary() 方法。使
用该类的任何代码将不受这一变更的影响,因为它仍然使用相同的语句 objectInstance.salary =
10 来访问该特性(property)。
在 AS3 中,当使用这种式样的 setter 和 getter 时,有一个惯例是在变量名称前添加一个下划
线。
接口
在 PHP 和 AS3 中,接口的工作方式几乎相同。一个显著的差异是在 PHP 中,你可以定义方法
以及常量,而在 AS3 中,你只能定义方法。然而,你可以定义 setter/getter:
1: public interface IEmployee {
2:
3: public function set salary(value:int);
4: public function get salary():int;
5: public function get income():int;
6: }
异常
正如在 PHP 中的那样,AS3 能够支持异常处理:
1: try {
2:
3: } catch(e:Error) {
4:
5: } finally {
6:
7: }
8:
9: //throwing an exception
10: throw new Error("Some error");
36. 在 AS3 中,对于所有错误来说,Error 是顶级类。你可以创建自己的错误以扩展该类,或你可
以使用现有的子类。
对象类型的转换和测试
有时,你希望将一个对象强制转换为一种不同的类型,或希望检查对象类型。在 PHP 中,你可
以使用 instanceof 检查对象的类型,而在 AS3 中,你可以使用 is 检查对象的类型。为了进行类
型强制转换,在 AS3 中,你可以使用两种不同的语句。
1: class A {};
2:
3: class B extends A {};
4:
5: var b:A = new B();
6: //casting
7: var c:B = b as B;
8: //or
9: var d:B = B(b);
10:
11: //checking the type of an variable
12: if (b is A)
13: trace(true);
14: if (b is B)
15: trace(true);
变量作用域
前面我们已经了解了变量、函数和类是如何在 Flex 和 AS 3 中工作的,现在是讨论变量作用域
的时候了。在 PHP 中,通常你具有两个作用域:全局(global)(在文件级别定义的变量)和
本地(local )(在函数内部定义的变量)。
在 Flex 中,共有 5 个作用域:函数体(function body)、实例方法体(instance method
body)、静态方法体( static method body)、类体(class body)和全局作用域(global
scope)。向这些作用域添加访问修饰符(public/private/protected/internal)使得事情比在 PHP 中
变得更为复杂。
37. 作用域可以嵌套。在本例中包含的作用域的变量/函数/成员均可以被嵌套的变量/函数/成员使
用。例如,当你在另一个函数体的内部声明一个匿名函数时,在 AS3 中,所有在外部函数定义
的变量可以在嵌套函数的内部使用。在 PHP 中,你必须传递你希望使用的变量,或添加 use 语
句:
1: //php code
2: function a() {
3: $a = 1;
4: $b = 2;
5:
6: function b() use ($a, $b) {
7:
8: }
9: }
10:
11: //AS3 code
12: function a():void {
13: var a:int = 1;
14: var b:int = 2;
15:
16: function b():void {
17: //variables a and b are available here
18: }
19: }
当你在一个未命名类包的内部声明一个函数时,其将被放置在全局作用域之中并且其对所有代
码均为可用的。然而,尽管在一个类包之外声明的任何函数仍然位于全局作用域,但其只能对
来自相同文件对代码是可见的。
数组
在 AS3 中,数组与 PHP 的情况非常相似,仅有一处差异:在 AS3 中,一个数组仅具有数字下
标。如果你希望创建一个关联数组,你可以使用 Object 类。如果你希望创建一个 key 是对象
(而非字符串)的哈希图(hash map)时,你可以使用 Dictionary 类。你可以使用 Array 类创建
38. 数组,也可以创建多维数组。对于 Object 和 Array ,你均可以使用文字定义。让我们来看看若
干范例:
1: var myArray1:Array = new Array(1, 2, "some string");
2: //creates an array with three elements: 0->1, 1->2, 3->some string
3:
4: //literal definition for an array
5: var myArray2:Array = [1, 2, 3];
6: //ading two more elements
7: myArray2.push(4,5);
8:
9: //a hash map, similar to associative arrays in PHP
10: var myMap:Object = new Object();
11: myMap.name = "Flex";
12: //literal definition of a map
13: var myMap2:Object = {name:"Flex"};
14:
15: //using Dictionary class
16: var dic:Dictionary = new Dictionary();
17: var anObject:Object = new Object(); //creating the key
18: dic[anObject] = "some value"; //adding a value to Dictionary
你具有添加元素或删除元素的所有期望的方法,包括 push、shift、pop、unshift 和 splice。
Concat 可以用于将数组添加至另一个数组。在前面的范例中,你可以看到我是如何使用 push
来添加另外两个元素至一个数组。
数组具有不固定的长度。其长度随着你添加更多元素而增加。在 PHP 中,你可以使用“[]” 在数
组的结束部分添加一个新的元素。AS3 具有相似的方法,该方法使用数组的 length 特性
(property )(你也能够使用 length 特性(property )来声明数组的长度)。
1: var array:Array = new Array();
2: array[array.length] = 1;//array has the values: 1
3: array[array.length] = 23;//array has the values: 1, 23
40. 为了调用在一个命名空间定义的变量或方法,你可以使用命名修饰符运算符“::”。假设你在一
个名称为 online 的命名空间中定义了一个名称为 myMethod() 的方法,你就可以使用语句
objectInstance.online::myMethod() 访问该方法。对变量来说,情形也是相同。有时,你可能需要
使用那些利用命名空间名称限制的变量或方法。你可以在相应的作用域打开该命名空间并且摆
脱该命名修饰符运算符的限制。你可以使用 use namespace namespaceidentifier 指令实现这一目
的。例如:
1: public function doSomething() {
2: use namespace online;
3: //call the method defined in that namespace:
4: myMethod();
5: }
你可以传送命名空间,例如,你可以从一个方法中返回一个命名空间,以便允许该调用代码使
用它来限定一个方法或成员。
现在,让我们来创建两个命名空间,它们可以用于在运行时改变类的行为。首先,我将定义这
两个命名空间(我将为每个命名空间提供一个文件):
1: // ActionScript file online.as
2: package org.corlan {
3: public namespace online = "http://corlan.org/apps/online";
4: }
1: // ActionScript file offline.as
2: package org.corlan {
3: public namespace offline = "http://corlan.org/apps/offline";
4: }
接下来,我将使用这两个命名空间创建一个能够存储一个对象的类。根据其链接状态,该类可
以在本地存储该对象(例如,使用本地存储设备),或以远端方式在服务器上存储该对象(使
用 REST 服务)。当某些代码需要使用该类时,有趣的部分将会出现。该调用的代码完全不关
心该方法,它只希望将该对象存储起来。
通过使用这两个命名空间,我将创建一个类,该类具有两个名称均为 save()的方法,每个方法
定义于其中一个命名空间。下一步,我将创建一个私有变量,该变量存储了当前的命名空间,
根据因特网连接的状态可以使用该命名空间。调用程序可以使用 getter 访问当前命名空间,并
且使用它来调用 save() 方法。再一次强调一下,调用程序不知道所有的这些 internal,而且不知
道这些命名空间,它也不关心这些命名空间。让我们看一下 PersistObject 代码:
1: package org.corlan {
41. 2: import flash.events.Event;
3:
4: public class PersistObject {
5:
6: private var _mode:Namespace = offline;
7:
8: public function PersistObject() {
9:
10: }
11:
12: online function save(object:Object):void {
13: //save the object back to server
14: trace("online");
15: }
16:
17: offline function save(object:Object):void {
18: //save the object locally
19: trace("offline");
20: }
21:
22: private function connectivityChanged(e:Event):void {
23: //here the mode can be changed from offline to online
24: //and vice-versa
25: }
26:
27: public function get mode():Namespace {
28: return this._mode;
29: }
30: }
42. 31: }
下面的代码片段使用了该类。该代码段非常简单,并且行内的注释给出了说明。
1: //creating an object that we want to be stored
2: var object:Object = {book:"Ulysses", author:"James Joyce"};
3: //create an instance of PersitObject
4: var persistenceObject:PersistObject = new PersistObject();
5: //get the current namespace
6: var currentMode:Namespace = persistenceObject.mode;
7: //use the namespace we retrieved to qualify the save method()
8: persistenceObject.currentMode::save(object);
命名空间可访问性
你可以使用与变量或方法相同的访问修饰符:public, internal, protected 和 private (对于在类包级
别定义的命名空间,你可以只使用 public 和 internal)。将这一点与命名空间定义的位置结合起
来,你将对程序中命名空间的可见性具有很强的控制能力。
与 XML 的配合
在 PHP 中,通过本机函数或附加扩展对 XML 提供了大量支持功能。在 AS3 中,本机有两个类
可以表示 XML:XML 和 XMLList。AS3 能够基于 W3C DOM (你可以使用像 children()、
appendChild()、parent()、 insertChildBefore() 等方法) 实现 XML 类。当你利用 XML 进行编程
时,你应该了解如何使用 E4X。E4X (ECMAScript-for-XML) 是 ECMA-262 语言的一种扩展,
该扩展是通过 AS3 实现的。你可以使用 XML 来表示一个 XML 文档。即使在只有一个子类的
情形下,任何来自该文档的节点都将包含于一个 XMLList。
你可以使用下列任何方法创建一个 XML 对象:
1. 使用文字形式编写 XML。
2. 创建一个 XML 实例,然后从一个外部文件导入该 XML 实例。
3. 创建一个 XML 实例,然后使用点记法(dot notation)以便添加/改变相应的结构:
1: var author:XML = <author/>;
2: author.@id = 1; //setting an attribute called id and its value
3: //adding two child nodes to author:
4: author.name = "Mihai Corlan";
43. 5: author.article = "Flex for PHP developers";
6:
7: //this code is equivalent with:
8: var author:XML = <author id="1">
9: <name>Mihai Corlan</name>
10: <article>Flex for PHP developers</article>
11: </author>;
通过使用 E4X,你可以根据基于节点名称或属性值的创建条件很容易地找到节点。你可以使用
递减运算符 “..” 查询具有给定名称的所有节点(例如,为了查询所有程序节点,你可以使用语
句:programs..program)。你可以使用“@” 运算符 (例如, programs..program.(@id==2))创建
基于属性的条件。最后,通过使用点符号,你可以在节点之间浏览(应该记住即使在只有一个
子类的情形下,任何子类均被看作一个 XMLList)。下面你将看到使用 E4X 与 XML 配合的范
例:
1: var programs:XML = <root>
2: <program id="1">
3: <name>Flex</name>
4: </program>
5: <program id="2">
6: <name>ActionScript 3</name>
7: </program>
8: <program id="3">
9: <name>AJAX</name>
10: </program>
11: </root>;
12:
13: //retrieving the second program node and printing its name
14: trace(programs.program[2].name[0]);
15: //retrieving all the program nodes:
16: var list:XMLList = programs..program;
17: //retrieving all the program nodes that have an id attribute equal to 2
44. 18: var list:XMLList = pograms..program.(@id==2);
动态 ActionScript
还记得 AS3 的定义吗?在该定义中,我给出了 AS3 是一种动态脚本语言的陈述。让我们更进一
步地讨论该功能。动态表示通过添加或删除方法或成员可以在运行时修改一个对象。系统能够
将新的方法添加至类的本身(并且任何由该类创建的对象将具有这些方法)。你甚至可以从头
创建新类(使用 protoype 特性(property))。AS3 具有内置的动态对象,例如 Object,而 Flex
具有另一个动态对象例子:ObjectProx。
如果你想知道为什么该功能首先出现,其答案是非常简单的:ActionScript 的早期版本不具有
AS3 今天提供的所有功能和 OOP。我必须说明,以我的经验来看,没有多少开发人员使用 AS3
的动态功能。这里有下面几个原因。首先,动态成员的访问速度要慢于固定成员。其次,你的
代码更易于出现软件缺陷(例如,没有编译时间错误检查)。
你可以不局限于使用内置类;你能够在类定义中使用 dynamic 修饰符创建动态对象:
1: dynamic public MyDynamicObject {
2:
3: }
现在,通过使用你刚才定义的类,你可以在运行时添加成员(记住所有动态实例变量均为
untyped 和 public):
1: var a:MyDynamicObject = new MyDynamicObject();
2: a.author = "Mihai Corlan";
你可以使用 for-each-in 循环语句将一个动态类的所有成员遍历一次。下面是如何从前面的范例
中显示成员的代码:
1: for each (var element:* in a) {
2: trace(element); //displays Mihai Corlan
3: }
如果你希望获得成员名称而不是其值,你可以使用 for-in 循环语句:
1: for (var memberName:* in a) {
2: trace(memberName); //outputs author
3: trace(a[memberName]); //outputs Mihai Corlan
4: }
45. Flex 是异步的
到目前为止,我们已经讨论了许多 Flex 功能,并且其中大部分功能与其 PHP 的对等部分非常
相似。但是,Flex 的异步特性与 PHP 相比具有相当的差异。停止与之对抗并且顺其自然,了解
这一点非常重要。
“Flex 是异步的”这句话的含义是什么?假设你创建一个 Flex 应用程序,并且在该应用程序载入
浏览器之后,用户可以选择从另一个站点下载图片。你也许使用一个 URLLoader 类来实现这一
任务。当你执行 load() 方法时,在代码的下一行,你将不会拥有相应数据。在 load() 调用等待
相应数据已经载入之后,脚本不会暂停。取而代之的是脚本的执行将被恢复。作为一个程序
员,你可以使用内置的 AS3 事件系统处理这一异步特性。如果你熟悉 AJAX 编程,这与你在执
行一个 AJAX 调用时所做的事情相似:你提供一个回调函数,并且当数据到达时,该回调函数
将被调用并且你可以访问相应的下载数据。
回到 ULRLoader 范例,你可以为 result 事件添加一个事件侦听器。这是一个函数,当数据载入
之后,它将被调用。
1: function loadPic():void {
2: var loader:URLLoader = new URLLoader();
3: loader.dataFormat = URLLoaderDataFormat.BINARY;
4: //adding the event handlers or listeners
5: loader.addEventListener(EventComplete, picLoaded);
6: loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, picError);
7: //starting the loading
8: loader.load(new URLRequest("http://some_url"));
9: }
10:
11: //event handler for
12: function picLoaded(event:Event):void {
13: //get the data from the loader object
14: //use the target property to get the loader object
15: (event.target as URLLoader).data;
16: }
17:
18: //event handler for the error event