Web

关于ActionScript

Flying
2013-06-06 / 0 评论 / 128 阅读 / 正在检测是否收录...

ActionScript 是一种运行在 Flash player 环境下的编程语言,它为 Flash 影片和应用程序提供交互、数据操作等功能。Flash Player 通过其虚拟机执行ActionScript、ActionScript 代码通常通过编译器(Flash Professional、Flex Builder、Flex SDK 或 Flex Data Services 中的)编译成二进制代码。编译好的代码内置于 SWF 中,该代码有 Flash Player 的运行环境下被执行。

ActionScript 3.0 作为 ActionScript 最新版本新增了不少功能。本文仅以 ActionScript 3.0 为例讲解。

ActionScript 3.0 简介

相对于 ActionScript 2.0,ActionScript 3.0 为哪些熟悉 OOP 编程的开发人员提供健壮的模式。

  1. 语法方面的增强和改动

    • 引入了 package(包) 和 namespace(命名空间) 两个概念。其中 package 用来管理类定义,防止命名冲突,而 namespace 则用来控制程序属性方法的访问。
    • 新增内置类型 int(32 比特整数),uint(非负 32 比特整数),用来提速整数运算;
    • 新增 * 类型标识,用来标识类型不确定的变量,通常在运行时变量类型无法确定时使用。 在 ActionScript 2.0 中这种情况下需要用 Object 赖作为类型表识;
    • 新增 isas 两个运算符来进行类型检查。其中 is 代替 ActionScript 2.0 中的 instanceof 来查询类实例的继承关系,而 as 则是用来进行不抛错误的类型转换。
    • 新增 in 运算符来查询某实例的属性或其 prototype 中是否存在指定名称的属性。
    • 新增 for each 语句来循环操作 Array 及 Object 实例。
    • 新增 const 语句来声明常量。
    • 新增 Bound Method 概念。当一个对象的方法被付值给另外一个函数变量时,此函数变量指向的是一个 Bound Method,以保证对象方法的作用域仍然维持在声明此方法的对象上。这相当于 ActionScript 2.0 中的 mx.util.Delegate 类, 在 ActionScript 3.0 中这个功能完全内置在语言中,不需要额外写代码。
    • ActionScript 3.0 的方法声明中允许为参数指定默认值(实现可选参数)。
    • ActionScript 3.0 中方法如果声明返回值,则必须明确返回。
    • ActionScript 2.0 中表示方法没有返回值的 void 标识, 在 ActionScript 3.0 中变更为 void
  2. OOP 方面的增强

    通过类定义而生成的实例, 在 ActionScript 3.0 中是属于 Sealed 类型,即其属性和方法无法在运行时修改。这部分属性在 ActionScript 2.0 中是通过类的 prototype 对象来存储,而在 ActionScript 3.0 中则通过被称为 Trait 的概念对象存储管理,无法通过程序控制。这种处理方式一方面减少了通过 prototype 继承链查找属性方法所耗费的时间(所有父类的实现方法和属性都会被直接复制到对应的子类的 Trait 中),另一方面也减少了内存占用量,因为不用动态的给每一个实例创建 hhahtable 来存储变量。如果仍然希望使用 ActionScript 2.0 中类实例在运行时的动态特性,可以将类声明为 dynamic

  3. API 方面的增强

    • 新增 Display API, 使 ActionScript 3.0 可以控制包括 ShapeImageTextFieldSprite、MovieClipVideoSimpleButtonLoader 在内的大部分 DisplayList 渲染单位。这其中 Sprite 类可以简单理解为没有时间轴的 MovieClip,适合用来作为组件等不需要时间轴功能的子类的基础。而新版的 MovieClip 也比 ActionScript 2.0 多了对于 Scene(场景)和 Label(桢标签)的程序控制。另外,渲染单位的创建和销毁通过联合 new 操作符以及 addChild/removeChild 等方法实现,类似 attachMovie 的旧方法已被舍弃,同时以后也无须去处理深度值。
    • 新增 DOM Event API,所有在 DisplayList 上的渲染单位都支持全新的三段式事件播放机制, Stage 为起点自上而下的播报事件到 target 对象,此过程称为 Capture Phase),然后播报事件给 target 对象(此过程称为 Target Phase),最后在自下而上的播报事件(此过程称为 Bubbling Phase)。
    • 新增内置的 Regular Expressions(正则表达式)支持,使 ActionScript 3.0 能够高效地创建、比较和修改字符串,以及迅速地分析大量文本和数据以搜索、移除和替换文本模式。
    • 新增 ECMScript for XML(E4X)支持。 E4X 是 ActionScript 3.0 中内置的 XML 处理语法。 在 ActionScript 3.0 中 XML 成为内置类型,而之前的 ActionScript 2.0 版本 XML 的处理 API 转移到 Flash.xml.*包中,以保持向下兼容。
    • 新增 Socket 类,允许读取和写入二进制数据,使通过 ActionScript 来解析底层网络协议(比如 POP3, SMTP, IMAP, NNTP)等成为可能,使 Flash Player 可以连接邮件服务器和新闻组。
    • 新增 Proxy 类来替代在 ActionScript 2.0 中的 Object.__resolve 功能。
    • 新增对于 Reflect(反射)的支持,相关方法在 Flash.util.* 包中。
  4. 与 ActionScript 2.0 类的语法差别

    其实,ActionScript 3.0 类与 ActionScript 2.0 的语法差别不是很多,因此如果你是一个有经验的 ActionScript 2.0 程序员,基本上不用担心。下面说就我知道的差别做一个小结。

    • ActionScript 3.0 中引入了包的概念,主要是解决类名称冲突,这一点和 Java 类似。 在 ActionScript 3.0 类中必须在包中声明。 而 ActionScript 2.0 是用类的完全限定的名称来实现相同的功能,类不能用 public 修饰;
    • ActionScript 3.0 中可以使用命名空间,命名空间好比是自定义是类成员访问符,声明之后用 using 命名空间来访问,这一点和 C# 相似。
    • ActionScript 3.0 中使用 private 属性只对对同一类中的引用可见。 而 ActionScript 2.0 中 private 属性允许子类访问超类中的私有属性。
    • ActionScript 3.0 中标记为 private 的属性在编译时和运行时都不可用,而在 ActionScript 2.0 中 private 关键字只在编译时禁止访问。
    • ActionScript 3.0 中 protected 属性是 ActionScript 3.0 中的新增属性,对同一类及子类中的引用可见,这一点类似于 ActionScript 2.0 的 private 关键字。
    • ActionScript 3.0 中对类属性时可以直接赋值等同于构造函数中进行初始化,即使类属性是数组或对象也如此。ActionScript 2.0 中如果属性是数组或对象,很可能将它们静态化。点击此处下载源文件。
    • ActionScript 3.0 中允许在同一类中定义同名的静态属性和实例属性,新增静态常量和 const 关键字。
    • ActionScript 3.0 类的方法作为参数传递给函数或值从函数返回时,this 总是引用实现方法的实例。 但 ActionScript 2.0 并不总是这样,因此很多时候不得不在函数外部声明地个变量引用 this,甚至使用 Delegate。点击此处下载源文件。
    • ActionScript 3.0 中子类覆盖父类方法必须使用 override 关键字。

基本开发流程

Flash 和 Professional Flex Builder 下面我们就用 Flex Builder 来编写并运行第一个 ActionScript 3.0 程序。

步骤

ActionScript 3.0 最好的 IDE 应该是 Flex Builder。下面我们就用 Flex Builder 来编写并运行第一个 ActionScript 3.0 程序。

  1. 新建工程。点选工具栏中的 New 按钮,从弹出的菜单中选择 ActionScript Project。如下图:

    fb_menu.gif

    在弹出的对话框中输入 HelloWorld 作为项目名称。单击下一步,主程序文件名默认下为 HelloWorld,输出文件夹名为 bin。我们不作更改,按 Finish 确定。如下图:

    fb_project.gif

  2. 编写代码。打开 Flex Builder 生成的 HelloWorld 文件,可以看到输入 Flex Builder 已经为我们编写好了 HelloWorld 类的架构,我们只需添加下列代码(绿色部分):

    package {
      import Flash.display.Sprite;
      import Flash.text.TextField;
      public class HelloWorld extends Sprite
      {
        public function HelloWorld()
        {
          var tfResult:TextField=new TextField();
          tfResult.text="Hello World";
          addChild(tfResult);
        }
      }
    }

    在 ActionScript 3.0 中你必须用 addChild 方法将 display 对象加载到容器中,这一点和旧版明显不同。

  3. 运行测试。按 Ctrl + S 保存文件,在 Navigation 视图中右键单击该文件,然后点击 run 按钮运行程序。如下图:

    fb_run.gif

    我们将在浏览器中看到 HelloWorld的问候。

在 Flex 中使用 ActionScript 的三种方法

在 Flex 中使用 ActionScript,其实和在网页中使用 JavActionScriptcript 等脚本文件类似,主要有三种方式。

Flex 的核心是 MXML 和 ActionScript。MXML是用于为 Flex 应用程序进行用户界面组件布局,它属于表示层,最终要编辑成 ActionScript 并生成 ActionScript 类文件在 Flash Player 上运行。

如果你是个 Java 开发者就很好理解这一点,MXML 就好比是 JSP/Struts/JSF,它们最终都会编辑成 Java 类文件并在具备 Java 虚拟机环境的浏览器上运行。所以说,Flex最核心的还是 ActionScript。在 Flex 中,ActionScript 是类库的方式出现的,该类库包含组件(容器和控件)、管理器类、数据服务类和所有其他功能的类。

其实和在网页中使用 JavaScript 等脚本文件类似,主要有三种方式。

  1. 内联方式
    这种方式直接将 ActionScript 方法作为事件的属性值,当然这种方法一般只有一行,相对简单。如果要给方法传递对数,这种方法就不可取了。

    <!--main.mxml -->
    <?xml version="1.0" encoding="utf-8"?> 
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> 
      <mx:Button label="Say Hello" click="sayHello('Flying')"/> 
        <mx:Script> 
          <![CDATA[ 
            import mx.controls.Alert; 
            private function sayHello(param_name:String):void { 
              Alert.show("Hello, "+param_name); 
            }   
          ]]>
        </mx:Script> 
    </mx:Application>
  2. 级联方式
    这种方式将 ActionScript 方法放入<mx:Script></mx:Script>代码块中,然后将方法作为事件的属性值,并可以在调用方法时传递参数,从而做到了 ActionScript 方法在一个文件中的重用。

    <!--main.mxml -->
    <?xml version="1.0" encoding="utf-8"?> 
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> 
    <mx:Script source="myFunction.as"/>  
      <mx:Button label="Say Hello" click="sayHello('Flying');"/> 
    </mx:Application>
  3. 外联方式

    // myFunction.asimport
    mx.controls.Alert;
    private function sayHello(param_name:String):void {
      mx.controls.Alert.show("Hello, "+param_name);
    }

    上述方式为 ActionScript 方法单独新建一个 ActionScript 文件,然后设置该方法为被调用文件的 Script 元素的 source 属性值,并可以在调用方法时传递参数,此文件可以在多个文件调用,从而实现了 ActionScript 方法在多个文件中的重用。

ActionScript 3.0 基础

变量和数据类型

变量都有相应的数据类型。 在 ActionScript 3.0 中,新增了 intuint 简单数据类型。int 型变量主要用作循环计数器, 而 uint 型变量为无符号的 32 位整型,主要用作引用 RGB 色彩值。注意它们取整时将直接舍弃小数部分。看下面代码。

package {
  import Flash.display.Sprite;
  public class VariableDemo extends Sprite {
    public function VariableDemo() {
      for (var i: int = 1; i < 6; i++) {
        trace(i);
      }
      var r: uint = 255;
      trace(i);
      var n1: int = 9.9;
      trace(n1);
      var n2: uint = 9.9;
      trace(n2);
    }
  }
}
> 注意:*void*简单数据类型是小写的。至于新增的复杂数据类型,有 RegExp  和 XMLList。

运算符和表达式

和 ActionScript 2.0 中的一样,ActionScript 3.0 运算符对应相应的表达式。

package { 
  import Flash.display.Sprite; 
  public class EODemo extends Sprite 
  { 
    public function EODemo(){ 
    // 数值运算符 
    var price:Number=400+50; 
    trace(price); 
    price=price+50; 
    trace(price); 
    price=price*1.2; 
    trace(price); 
    price=price/2; 
    trace(price); 
    price=price%4; 
    trace(price); 
    // 字符串运算符 
    var bookTitle:String="ActionScript 3.0 "; 
    bookTitle = bookTitle+"& Flex 2.0 实战"; 
    trace(bookTitle); 
    // 赋值运算符 
    price+=200; 
    trace(price); 
    // 关系运算符 
    trace(price>100);    
    // 逻辑运算符 
    trace(price>100&&price<100);  
    // 条件运算符 
    trace((price>100) ? "大于 100" : "不大于 100"); 
    // new 运算符 
    var fruits:Array=new Array("apples", "oranges", "bananActionScript"); 
    // 数组访问运算符[] 
    trace(fruits[0]);       
    // 点 (.) 运算符 
    trace(fruits.length);   
    // 按位运算符(|和>>) 
    fruits.sort(Array.CASEINSENSITIVE | Array.DESCENDING); 
    trace(price>>2); 
    } 
  } 
}

流程语句

ActionScript 3.0 中新增了 for...each 循环语句,它最大的优点就是可以直接遍历 XML 或 XMLList 对象。

虽然都是循环语句,但 for...each 循环语句与 for...in 不同。

  1. for...each 循环语句遍历对象,循环变量包含属性值,而 for...in 循环语句无序遍历对象,循环变量包含属性名。

    package { 
      import Flash.display.MovieClip; 
      public class StatementDemo extends MovieClip 
      { 
        public function StatementDemo(){ 
          var i; 
          var myObj:Object = {x:20, y:30,z:50}; 
          for (i in myObj) 
          { 
            trace(i); 
          } 
          for each (i in myObj) 
          { 
            trace(i); 
          } 
        } 
      } 
    }
  2. for...each 和 for...in 循环语句都可以顺序遍历数组的元素,但前者循环变量包含元素值,而后者循环变量包含元素下标。

    package { 
    import Flash.display.MovieClip; 
      public class StatementDemo extends MovieClip 
      { 
        public function StatementDemo(){ 
          var i; 
          var myArray:Array = ["one","two","three"]; 
          for (i in myArray) 
          { 
            trace(i); 
          } 
          for each(i in myArray) 
          { 
            trace(i); 
          } 
        } 
      } 
    }

数组

数组是 ActionScript 的核心类之一,ActionScript 3.0 中新增了不少数组的方法,比如 indexOflastIndexOfeverysomemapforEach 等。看下面实例代码:

package { 
  import Flash.display.Sprite; 
  public class ArrayDemo extends Sprite 
  { 
    public function ArrayDemo() 
    { 
      var production:Array=new Array(); 
      production[0]=1000; 
      production[1]=1200; 
      production[2]=1300; 
      production[3]=1200; 
      production[production.length]=1000; 
      production.push(1250); 
      trace(production.length);               // 6 
      trace(production[3]);               // 1200 
      // 用 lastIndexOf 方法返回某个元素值第一次出现的位置 
      trace(production.indexOf(1200));            // 1 
      // 用 lastIndexOf 方法返回某个元素值最后一次出现的位置 
      trace(production.lastIndexOf(1200));            // 3 
      // 用 new 运算符声明一个关联数组(用{}运算符会更简捷) 
      var salesVolume:Array=new Array(); 
      salesVolume["Jan"]=800; 
      salesVolume["Feb"]=1200; 
      salesVolume["Mar"]=1200; 
      salesVolume["Apr"]=1100; 
      salesVolume["May"]=800; 
      salesVolume["Jun"]=1200; 
      // 使用数组访问运算符 ([]) 访问数组元素值 
      trace(salesVolume["Apr"]);              // 1100 
      // 使用点运算符 (.) 访问数组元素值 
      trace(salesVolume.Apr);             // 1100 
      // 用 new 运算符声明一个以对象作为元素的关联数组,对象的属性为元素名,对象的属性值为元素值 
      var sale:Array=new Array(); 
      sale.push({month:"Jan",volume:800}); 
      sale.push({month:"Feb",volume:1200}); 
      sale.push({month:"Mar",volume:1200}); 
      sale.push({month:"Apr",volume:1100}); 
      sale.push({month:"May",volume:800}); 
      sale.push({month:"Jun",volume:1200}); 
      // 用 every 方法遍历数组,检验其所有元素是不是符合某个标准,全部满足才返回 true 
      trace(sale.every(everySale));               // true 
      // 用 every 方法遍历数组,只要有一个符合某个标准就返回 true 
      trace(sale.some(someSale));             // true 
      // 用 filter 方法遍历数组,返回所有符合某个标准的元素组成的数组 
      trace(sale.filter(filterSale).length);          // 4 
      // 返回由所有元素执行 map 的参数函数的返回值组成的新数组 
      var mapArr:Array = sale.map(mapSale); 
      trace(mapArr);      //  900,1300,1300,1200,900,1300 
    // 返回所有元素执行 forEach 的参数函数的值 
      sale.forEach(traceSale);    // Jan (800) Feb (1200) Mar (1200)... 
    } 
    private function everySale(item:*, index:int, array:Array):Boolean 
    { 
      return (item.volume >600); 
    } 
    private function someSale(item:*, index:int, array:Array):Boolean 
    { 
      return (item.volume >1100); 
    } 
    private function filterSale(item:*, index:int, array:Array):Boolean 
    { 
      return (item.volume >1000); 
    } 
    private function mapSale(item:*, index:int, arr:Array):int
    { 
      return item.volume+100; 
    } 
    private function traceSale(item:*, index:int, arr:Array):void 
    { 
     trace(item.month + " (" + item.volume + ")"); 
    } 
  } 
}

Point 对象

高中时几何和物理学得不好,老是记不住一些公式,因此做 Flash 游戏就头大。现在好了,ActionScript 3.0 中的 Flash.geom 包中包含了用于定义几何对象(如点、矩形和转换矩阵)的类,以后做 Flash 游戏就方便多了。

Point 对象定义一对笛卡尔坐标。它表示二维坐标系中的某个位置。其中 x 表示水平轴,y 表示垂直轴。

  1. 确定两点之间的距离

    // 定义 Point 对象
    var pt1:Point = new Point(0, 0);
    var pt2:Point = new Point();
    // 平移坐标
    pt2.offset(3,4);
    var dist = Point.distance(pt1, pt2);
    trace(dist);

    看出来没?这就是勾股定理的一个简单应用。

  2. 按指定的角度和距离移动显示对象

    ActionScript 3.0 以前按指定的角度和距离移动显示对象只能用 Math 类的方法,相当复杂。现在利用 Point 对象的 polar 方法方便地实现类似功能。看下面代码:

    import Flash.geom.*; 
    import Flash.events.*; 
    // 创建圆形 
    var size:uint = 100; 
    var roundObject:Shape = new Shape(); 
    roundObject.graphics.beginFill(0xFF0000); 
    roundObject.graphics.moveTo(size / 2, 0); 
    roundObject.graphics.curveTo(size, 0, size, size / 2); 
    roundObject.graphics.curveTo(size, size, size / 2, size); 
    roundObject.graphics.curveTo(0, size, 0, size / 2); 
    roundObject.graphics.curveTo(0, 0, size / 2, 0); 
    stage.addChild(roundObject); 
    // 移动圆形 
    var distance:Number = 100; 
    var angle:Number = 2 * Math.PI * (30 / 360); 
    var translatePoint:Point = Point.polar(distance, angle); 
    var dx:Number = translatePoint.x/5; 
    var dy:Number = translatePoint.y/5; 
    // 自定义侦听器函数 
    function onMove(e:Event):void { 
      var s:Shape = e.target as Shape; 
      s.x += dx; 
      s.y += dy; 
      if (s.x > 400) { 
        // 删除侦听器 
        roundObject.removeEventListener(Event.ENTER_FRAME,onMove); 
      } 
    } 
    // 注册侦听器 
    roundObject.addEventListener(Event.ENTER_FRAME, onMove);

    polar 方法可以将一对极坐标转换为笛卡尔点坐标。Point 对象中还有 addsubtract 等其它方法,大家参看帮助文件吧。

变量和数据类型

变量都有相应的数据类型。 在 ActionScript 3.0 中,新增了 intuint简单数据类型。int 型变量主要用作循环计数器, 而 uint 型变量为无符号的 32 位整型,主要用作引用 RGB 色彩值。注意它们取整时将直接舍弃小数部分。看下面代码。>

package {
  import Flash.display.Sprite;
  public class VariableDemo extends Sprite {
    public function VariableDemo() {
      for (var i: int = 1; i < 6; i++) {
        trace(i);
      }
      var r: uint = 255;
      trace(i);
      var n1: int = 9.9;
      trace(n1);
      var n2: uint = 9.9;
      trace(n2);
    }
  }
}
注意:void简单数据类型是小写的。至于新增的复杂数据类型,有 RegExp 和 XMLList。

使用 …(rest) 扩展 Event 参数对象属性

大家都知道,当发生事件时,Event对象将作为参数传递给事件侦听器,但 Event 对象将作为参数的属性是有限的。如果要传递特殊参数,一般会想到通过自定义事件类来实现。下面介绍一种更简单的方法:使用 ...(rest) 参数来扩展 Event 参数对象属性。

下面自定义了一个事件代理类:

package { 
  public class EventDelegate 
  { 
    public static function create(f : Function,... arg) : Function {     
      var isSame : Boolean = false;            
      var _f : Function = function(e : *,..._arg):void{                
        _arg = arg;              
        if(!isSame) {                    
          isSame = true;                   
          _arg.unshift(e);                 
        }                
        f.apply(null, _arg); 
      };   
      return _f;   
    }        
    public static function toString() : String {             
      return "Event Delegate";         
    }    
  } 
}

使用方法很简单:addEventListener(事件类型, EventDelegate.create(侦听器函数, 参数)),用法有点类似 AS2 中的 Delegate 类。

算法

一直以来数学学得不好,因此见了算法就头大,但有时又不得不面对。在实际开发中很多时候会作到算法。

递归

递归就是最常见的一种。下面我就来谈一谈自己的肤浅认识。

递归可以让一个函数从其内部调用其自身,递归和循环紧密相关,基本上能用循环就能用递归。一个典型示例就是计算阶乘,下面是其 ActipnScript 的代码。

// 阶乘 
function factorial(n) { 
  if (n > 1) { 
    return n * factorial(n - 1); 
  } else { 
    return 1; 
  } 
} 
trace("6! = " + factorial(6));

此处我们定义了 factorial 函数,并在其内部调用自己,最后当 n=1时就返回 1,不再调用从而实现阶乘运算。著名的高斯算法一般是用循环来实现的,其实也可以用递归来实现。看下面代码:

// 高斯算法 
function sum(min, max) { 
  if (max > min) { 
    return max + sum(min, --max); 
  } else { 
    return max; 
  } 
} 
trace("1 + 2 + ...+10=" + sum(1,10));

和循环一样,递归一定要有一个终结点,不然将导致计算机执行一个 无限死循环。

数组冒泡排序

数组排序有很多种算法。在冒泡算法中,较小的数组元素朝数组顶部逐渐冒上来,就像水中气泡上升一样,而较大的数组元素朝数组底部逐渐沉下去。这种算法用嵌套循环对整个数组数次遍历,每次遍历都比较数组中相邻的一对元素,如果它们以升序排列(或者值相等),那么它们的位置不变,否则交换它们的位置。看下面代码。

package {
  public class Array2 {
    public static function bubbleSort(array : Array) : void { // 冒泡排序
      for (var i : uint = 0;i < array.length - 1; i++) { // 外层循环
        for (var j : uint = 0;j < array.length - i - 1; j++) { // 内层循环
          if (array[j] > array[j + 1]) {
            var temp : uint = array[j];
            array[j] = array[j + 1];
            array[j + 1] = temp;
          }
        }
        print(i + 1, array);
      }
    }
    public static function print(time : uint,array : Array) : void {
      trace("第" + time + "次排序:");
      trace(array.toString() + "  ");
    }
  }
} 

用法很简单,如下所示。

Array2.bubbleSort(array); 
/* 
第 1 次排序: 
4,5,3,7,0,9   
第 2 次排序: 
4,3,5,0,7,9   
第 3 次排序: 
3,4,0,5,7,9   
第 4 次排序: 
3,0,4,5,7,9   
第 5 次排序: 
0,3,4,5,7,9  
*/

在冒泡算法中,外层循环的次数即是该算法的次数。

数组二叉查找算法

从数组中查找某一元素的最简单办法就是遍历数组中所有元素,然后和查找值比较,这种查找方式称作线性查找,它适用于未排序的小型数组。对于已排序的大型数组,可以使用二叉查找算法。

该算法首先找到数组中间位置的元素,并将其与查找值比较,如果相等,就返回该元素的索引;否则就将问题简化为查找数组的一半元素。如果查找值小于中间元素,就查找数组的前半部分,否则就查找数组的后半部分。看下面代码:

package {
  import Flash.display.Sprite;
  /**
   * @author Flying
   */
  public class Array2 extends Sprite {
    public function Array2() {
      var array : Array = [4, 5, 6, 7, 9, 13, 17];
      trace("13的索引位置: " + indexOf(array, 13));
    }
    /** 采用二叉查找算法 */
    public static function indexOf(array : Array, value : int) : int {
      var low : int = 0;
      var high : int = array.length;
      var middle : int;
      while (low < high) {
        middle = (low + high) / 2; // 计算中间元素的索引 
        print(array, middle); // 打印数组,用于跟踪查找过程 
        if (array[middle] == value)
          return middle;
        if (value < array[middle])
          high = middle;
        else
          low = middle;
      }
      return -1; // 没有找到该元素,返回-1 
    }
    private static function print(array : Array, middle : int) : void {
      for (var i : uint = 0;i < array.length; i++) {
        trace(array[i]);
        if (i == middle)
          trace("*");
        trace("  ");
      }
    }
  }
}
/* 
 4  5  6  7*  9  13  17   
 4  5  6  7  9*  13  17   
 4  5  6  7  9  13*  17   
 13 的索引位置:5 
 */

为方便测试,继承了 Sprite 类。

交换二维数组的行列

曾经用 Perl 做了一个项目,将 N 个某个目录下的 N 个 CSV 插入到 Sybase 数据库中,CSV是个交叉表,在处理时需要交换二维数组的行列。用 perl 来实现很简单,其实用 ActionScript 也不难。

代码如下:

package com.riafan.utils {
/**
  * class that contains static utility methods for manipulating and working
  * with Arrays
  * @author Flying
  * @version 1
  * @tiptext
  */
  public class ArrayUtil {
    /**
     * Switch the rows and columns of a 2d array array
     * @param arr The 2d array whose dimensions will be switched
     * @return A new array which contains items after switch
     */
    public static function switchDimensions(arr : Array) : Array {
      var newArr : Array = new Array();
      var rowMax : uint = arr.length;
      var colMax : uint;
      try {
        if(arr[0] is Array) {
          colMax = arr[0].length;
        }else {
          throw new TypeError("muse be a 2d array");
        }
      }
      catch (error : TypeError) {
        trace(error.message);
      }
      for (var col : uint = 0; col < colMax; col++) {
        var tmpArr : Array = new Array();
        for (var row : uint = 0; row < rowMax; row++) {
          tmpArr[row] = (arr[row][col]);
        }
        newArr[col] = tmpArr;
      }
      return newArr;
    }
  }
}

说明:switchDimensions方法用来交换初始二维数组的行列,并返回新的二维数组。比如说[[1, 2],[true, false], ['A','B']] 将返回 [[1, true, 'A'], [2, false, 'B']]

数组的最大两个元素求和

其实用数组的 sort 方法或 Math.max 方法不难求得数组最大两个元素的和,但有人觉得这样做性能不高。那我们就不使用这两种 ActionScript 方法,而通过比较遍历数组元素来实现。看下面代码:

function sumTwoLargest(arr:Array):int
{
  var count:int = arr.length;
  var largest:int;
  var secondLargest:int;
  var tmp:int;
 
  for (var i = 0; i < count; i++)   {     
    tmp = arr[i];     
    if (tmp > largest)
    {
      secondLargest = largest;
      largest = tmp;
    }
    else if (tmp > secondLargest && tmp < largest)
    {
      secondLargest = tmp;
    }
  }
  return largest + secondLargest;
}
 
var my_arr:Array = [1, 16, 16, 2, 18, 14, 18, 7];
trace(sumTwoLargest(my_arr));

注意第 13、14行,不能交换位置。这种算法是否真的性能更高,有待检验。

使用 ActiotScript 连接组合两个 Array

有两个关联数组,如何将其中一个关联数组添加到另外一个数组中,当然关联数组中得有一个相同的属性,该属性性对于源数组是唯一的,对于目标数组可以重复或为空。这就好像是 SQL 中用 inner join 关键字将两个表通过外键连接起来。看下面的代码:

package com.riafan.utils
{
  public class ArrayUtil
  {
    public static function innerJoin(targetArr:Array, 
      sourceArr:Array, key:String):void
    {
      var targetCount:int = targetArr.length;
      var sourceCount:int = sourceArr.length;
      for (var i:int = 0; i < targetCount; i++)
      {
        var targetItem:Object = targetArr[i];
        for (var j:int = 0; j < sourceCount; j++)
        {
          var sourceItem:Object = sourceArr[j];
          if (targetItem[key] == sourceItem[key])
          {
            for(var prop:String in sourceItem) 
            { 
              if(prop != key)
              {
                targetItem[prop] = sourceItem[prop];
              }
            } 
          }
        }
      }
    }
  }
}

注意到我们这里用三层循环,最里层循环将源数组中的所有属性添加到目标数组中,当然作为外键的数组在目标数组中已经存在就不用加了。

使用实例如下:

var students:Array = [
  {sid:201013018, sname:'Jim', sage:17, tid:2163},
  {sid:201013021, sname:'John', sage:18, tid:2163},
  {sid:201013025, sname:'Kite', sage:17, tid:2104}
];

var teachers:Array = [
  {tid:2163, tname: 'Smith', tage:30},
  {tid:2104, tname: 'Tom', tage:32},
  {tid:2102, tname: 'Jane', tage:31}
];

ArrayUtil.innerJoin(students, teachers, 'tid');

trace一下 students 数组,已经添加了老师的姓名和年龄。

ActionScript 3.0 设计模式类图

最近看了 ActionScript 3.0 Design Pattern 一书,该书主要介绍了常用的 ActionScript 3.0 设计模式,确实是一本 ActionScript 3.0 OOP 好书。不过,我发觉作者在介绍设计模式时,少了类图。以方便大家更好地理解 ActionScript 3.0 的设计模式,特意用 EA 画了此书中用到的设计模式类图。

  • Factory(工厂)模式

as_factory.gif

  • Singleton(单例)模式

as_singleton.gif

  • Decorator(装饰)模式

as_decorator.gif

  • Adapter(适配器)模式

as_adapter.gif

  • Composite(组合)模式

as_composite.gif

  • Command(命令)模式

as_command.gif

  • Observer(观察者)模式

as_observer.gif

  • Template(模板)模式

as_template.gif

  • State(状态)模式

as_state.gif

  • Strategy(策略)模式

as_strategy.gif

  • MVC 模式

MVC模式

4

评论 (0)

取消