您好,欢迎访问湖北信息工程学校官网!
  • 官方微信

您现在所在位置:首页>>教师频道>>特色课程

Flash ActionScript高级编程指南

来源:本站   作者:admin   更新时间:2012-03-08

Flash5的ActionScript里新增了许多诱人的函数与功能,使得闪客们得以更大的空间发挥他们高超的闪技。但同时也带来了不少新的困扰。下面我们就来讨论一下其中的一些难点和高级应用。如果您已经看过Flash5 ActionScript的联机帮助,或者对FlashActionScript(FAS)已经有了一定的了解的话,相信一定会从本教材中得到不小的收获的。

  注:本教材中的某些概念与Flash5 ActionScript的联机帮助中的内容有一定的出入,但笔者是在仔细分析了实现原理及经过了反复的测试后才作如此之改动的。笔者认为按新的概念更能抓住FAS语法的精髓,望读者予以理解。

一、动作脚本的语法规则

  自从Flash4加入了ActionScript——动作脚本编辑之后,Flash渐渐成为广大编程爱好者的又一块处女地。由于它的出现,许多用纯动画难以制作的场景编绘(比如飘雪)变得轻松写意。难怪乎许多动画爱好者要把ActionScript称为动画神枪手,指哪儿打哪儿。然而Flash4中的动作脚本毕竟初出茅庐,还有许多不近人意的地方。比如没有提供实现三维所必须的三角函数等等。这就使得不少初学者望而生畏,而高手们在编写复杂脚本时也不得不借助于第三方软件(比如Fast!)。

  在众望所归之下,Flash5终于推出了令人欣喜的脚本编辑器,而脚本的功能也与往日不可同日而语了。总的来说,新的ActionScript共有三项重大改进:
  - 与Java和C/C++几乎完全相同的语法风格;
  - 强大的库函数支持;
  - 包含直接代码编辑的集成脚本编辑器。
Flash ActionScript终于从尝试走向完善。

  废话暂时就先说到这儿,下面我们就来仔细讨论一下新的ActionScript的语法和书写规范。熟悉此项的大虾们也请看一下,说不定有你所不知道的呢!

废话暂时就先说到这儿,下面我们就来仔细讨论一下新的ActionScript的语法和书写规范。熟悉此项的大虾们也请看一下,说不定有你所不知道的呢!

1、脚本构成

  和C++语言一样,Flash脚本也是由对象和函数构成的,只不过FAS里的“对象”实际上是影片片段而已(FAS本身的对象含义不象C++中的对象,倒更象是结构体,因为它没有真正意义上的成员函数,此点我们以后细说)。但它们之间又有本质区别。

  首先,C++中的对象是由类实例化得来的,类可以被继承;而FAS中没有类的概念,所有的影片片段都是现成的,最多只能用attachMovie来动态生成一个非现成的影片片段,或用duplicateMovieClip来复制一个已有的影片片段。

  其次,C++中成员函数定义在类声明里,可以被重载,且没有先后顺序;而FAS中所有的函数都定义在某个特定的影片片段中,这也就是说,如果定义函数的那个影格未被播放,函数就不存在;另外FAS中的函数不能被重载,只能被覆盖,系统始终只保留最近被执行的脚本中的函数定义。比如:
  function myFunction(a) {
    trace("1");
  }
  function myFunction(a,b) {
    trace("2");
  }
  myFunction(1);
输出的是2而不是1。

  第三,FAS中的影片片段全都是嵌套的,有着深厚的层次关系,只要写明其路径,就能直接调用任何影片片段的成员函数或变量等;而C++中虽然也有对象的嵌套,但只有外层的对象可以直接调用内层对象的公有成员。

2、基本元素

  Flash脚本的基本元素有:变量、数组、对象、影片片段、影片片段)属性和函数。其中数组(一种特殊的对象)、对象(包括一些预定义对象,如Math)和自定义函数是Flash5中新增的内容。另外,老版本中的一些指令,象:
  setPropery (target,property,value);
等,我们现在也把它们称为函数。这样一来,其余就只剩下为数不多的一些关键词了,下面一一列出:
code:--------------------------------------------------------------------------------  #i nclude "..." var
  new delete
  if...else... ifFrameLoaded(...)...
  for(...)... for(...in...)...
  do...while(...) while(...)...
  break continue
  tellTarget(...)... with(...)...
  function ...(...){...} return
  on(...){...} onClipEvent(...){...}
--------------------------------------------------------------------------------
其中写明要{}的必须加上{},其他如if等在子过程只有一条语句时可省略{}。具体应用放到以后细讲。

3、语句书写格式

  Flash脚本的语句书写基本上与Java和C/C++语言相同。一条语句可分几行写,也可在同一行中写多个语句。不同之处有以下几点:

  (1)当不会与后一语句产生歧义时,语句末尾的“;”可以省略,但为了分隔同一行内的多个语句则必须要加。比如:
  a = b =
  c = 1
  trace(a); trace(b); trace(c);
不过我们为了统一起见,还是提倡一律如C语言那样加上分号。

  (2)变量、对象等无须定义(用var可定义其局部性)就可直接赋值使用,在未赋值前所有标识符都代表一个值为空的无类型变量。同一标识符可反复赋予不同类型的值。

  (3){}可以在任何地方成对加,但无任何实际效果(var只对函数体有效,这在后面会讲到),不象C/C++语言那样有局部作用域功能。比如:
  a = 1;
  if (a==1) {
    var a = 0;
  }
  trace(a);
将输出0,而在C/C++中会输出1。

  (4)函数可以在任何地方定义。甚至可以把一个函数定义在另一个函数的体内,就象下面这段程序:
  function myFunction1() {
    function myFunction2() {
      trace("2");
    }
    trace("1");
  }
  myFunction1();
  myFunction2();
将输出:
  1
  2
想想看如果把上面两个函数都定义成myFunction1会有什么结果?呵呵,结果是输出两个2,道理相信您一定想得明白。

二、点语法对象与目标路径的使用

  Flash ActionScript里最基础但也往往是最不容易搞清楚的恐怕就要算点语法对象和目标路径的使用了。不用说初学者常常不知道什么时候该用点语法对象,什么时候该用目标路径,就连能熟练运用三维显示技术的闪客高手有时也会因为概念混淆而用错,而这种错误又往往是最难发现的。所以,我把它放在第二节来介绍,同时也顺带讲一下与此密切相关的一些指令和函数的使用。

1、形式与含义

  形如 a.b.c 的表达形式即为点语法对象。它可以指代变量、数组和对象,也可以指代影片片段、属性和函数。比如:
  anyObject.a
  b[12].anyArray
  _root.anyMovieClip._x
  anyMovieClip.anyFunction
等都是合法的点语法对象形式。一般把这种表示形式与Java或C/C++语言中的类与成员、成员函数的表达相类比,但要注意的是FAS里更强调的是对象的层次,甚至还有_parent、_root之类相对路径的表示形式,它并没有继承等面向对象语言的标准概念。所以说把它理解为地址或者路径似乎更为恰当一些。

  形如 "a.b/c" 或 "a/b:c" 的为目标路径。它是一个字符串,可以指代变量和对象,也可以指代影片片段和属性,但不能指代数组和函数。它的分隔符可以有"."、"/"和":"三种,其中"/"后跟对象和影片片段,":"后跟变量和属性,"."则都可。不同的分隔符可以混合使用。比如:
  "anyObject:a"
  "_root/anyMovieClip._x"
等都是合法的目标路径形式。除了点语法对象是变量形式而目标路径是字符串形式这一根本区别外,它们所表示的含义完全相同,只是在使用上各有侧重而已。

2、具体使用

  当我们要对某一已知名称的变量或对象等操作时,应选择点语法对象表达。比如对变量或属性赋值:
  myMovieClip._xscale = 150;
  myObject.index = 16;
  _parent.myObjectArray[14].index = 10;
又比如调用函数:
  myMovieClip._x = myMath.square(length);

  当我们要对某一不定名称的变量或对象等操作时,应选择目标路径表达。比如对一批影片片段的某一属性赋值:
  for (i=1;i<10;i++) {
    set ("myMovieClip"+i+"._x",20*i); // 2.1式
  }

  有时我们又要把它们联合起来使用,以弥补点语法对象无法表达不定名称及目标路径无法表达数组和函数的不足。比如对一批影片片段下的某一对象数组赋值:
  for (i=1;i<10;i++) {
    eval("myMovieClip"+i+".myObjectArray")[0].index = i; // 2.2式
  }
这是Flash5中所提倡的用法。所以,从某种意义上来说,目标路径可以看作是在表示源代码。

  注意:有时点语法对象也被当成字符串来用,比如:a = anyMovieClip+"1",a的值就为"_level0.anyMovieClip1"。但本质上其实是系统在执行+"1"前自动内部执行了String(anyMovieClip)而已。其他情况也是一样。

二、点语法对象与目标路径的使用

3、相关函数与指令

  eval(expression)是与点语法对象和目标路径的使用有关的最常用的函数。它的实质是把目标路径转化成点语法对象。系统先把expression自动转化成字符串,相当于调用了一次String(expression),得到"expression_string"的形式。这时的"expression_string"必须为目标路径形式(否则无返回值),然后返回"expression_string"所指代的内容。

  为什么说是内容而不说是值呢?这是因为eval(expression)的返回值实际上是一个点语法对象。比如:
  eval("anyMovieClip"+1)._x = 200;
仍是合法的语句。如果说._x的左边是一个值似乎就不合适了。类似的如前面提到的2.2式。故对于形如:
  c = a+eval("b");
的理解(设b的值为1)不应为c=a+1,而应该是c=a+b。在处理形如上述2.1式的表达式时,Flash5推荐使用eval函数,即写成:
  for (i=1;i<10;i++) {
    eval("myMovieClip"+i)._x = 20*i;
  }
另当"expression_string"指代的是一个影片片段时,返回的是形同_level0.anyMovieClip的绝对路径形式,所以就不能使用如_root.eval(expression)之类的调用了。

  with与tellTarget也是与点语法对象和目标路径密切相关的指令。其中with是Flash5中新增加的。它们的使用格式相同:
  with (expression) {
    ...
  }
  tellTarget (expression) {
    ...
  }
expression可以是点语法对象,也可以是目标路径。对于with来说,expression可指代对象和影片片段,而对于tellTarget来说,expression只能是影片片段。

  虽然它们形式上极为相仿(尤其当expression同指影片片段时),但处理过程却并不相同。试比较(a非myMovieClip的成员变量):
  a = 1;
  with (myMovieClip) {
    trace(a); // 2.3式
  }

  a = 1;
  tellTarget (myMovieClip) {
    trace(a); // 2.4式
  }
前者输出1,而后者输出空值。原因是tellTarget对它所包含的语句中的所有变量、对象等都加上expression所表示的路径,而with只对其中expression所指对象或影片片段内已经定义过的变量、对象等加上expression所表示的路径。所以2.3式就相当于with外的:
  trace(a);
而2.4式就相当于tellTarget外的:
  trace(myMovieClip.a);
当然,如果a本就是myMovieClip的成员的话,二者的输出就一样了。

三、自定义函数的运用

  加入函数这一概念可能是Flash5对动作脚本语言的改进中最令闪迷们感到兴奋的内容了。因为在老版本的FAS中,为了实现三角函数之类的函数功能,Flash动画制作者们不得不在一大堆一大堆的set指令和call指令之中漫游,既不方便执行效率又低,而且还常常为了逃避该死的“200000行”限制(此限制已在Flash5中取消)而绞尽脑汁、机关算尽。现在有了函数,朋友们,我们还怕什么呢!

  可能有些熟悉Java或C/C++编程的闪客们要说了,函数不是很平常的概念嘛,有什么好讲的。其实没那么简单。既然笔者把它单独拎出长长的一节来作介绍,就一定有其不同寻常之处。下面我们就来看看它有什么神秘面纱。为了突出重点,本节只讲有关自定义函数的使用,系统指令函数及预定义对象的成员函数留待以后再讲。

1、自定义函数的形式及功能

  按照Flash5 ActionScript联机帮助中的正统解释,自定义函数的表达形式分为有名函数与无名函数两种。具体如下:
  function functionname([argument0, argument1,...argumentN]) {
    statement(s)
  }
  function([argument0, argument1,...argumentN]) {
    statement(s)
  }
其中有名函数可用来作为一般意义上的函数或自定义对象的构造函数;无名函数专门用来为自定义对象加载方法(即成员函数)。以下为这三种功能的实例:

  (1)用作一般函数(特点:可有返回值):
  function add10(x) {
    return x+10;
  }
  trace(add(10));

  (2)用作构造函数(特点:利用this为对象的成员赋值,this为必须):
  function box(l,t,r,b) {
    this.left = l;
    this.top = t;
    this.right = r;
    this.bottom = b;
  }

  (3)用作为自定义对象加载方法(特点:在构造函数名后加.prototype表示加载方法,这也是必须的):
  box.prototype.area = function() {
    return (this.right-this.left)*(this.bottom-this.top);
  }

三、自定义函数的运用

2、函数语法的深入分析

  精通面向对象语言的朋友一看上面的语法说明就一定会马上产生这样的感觉:有名函数在用作构造函数时怎么那么别扭,既然已经是构造函数了,为什么对成员赋值还非得加上this?为对象加载方法又为什么必须要加上一个讨厌的.prototype,而且看上去好象还非要用无名函数来定义。不忙,其实只要知道了其中的实现机理,这一切问题就雪融冰释了。

  第一点认识:所有的有名函数实现原理都一样。不论是一般的函数也好,对象的构造函数也好,它们在使用时没有任何区别。你完全可以用任何一个函数(包括系统预定义的函数)通过new来构造一个对象。比如下面这条语句在语法上是完全正确的:
  a = new stop(); // stop是FAS中的系统指令函数
在此例中,你甚至还可以为stop“对象”加上方法:
  stop.prototype.func = function() {return 1;} // 3.1式
  a = new stop();
  trace(a.func()); // 输出 1
当然,我们不提倡把系统预定义函数当成构造函数来用,只是想以此说明所有的有名函数在语法上并没有什么区别。

  第二点认识:无名函数与有名函数之间的差别只是有没有名字。这样的提法似乎有点出乎意料,不过事实就是这样。FAS里的函数名归根结底也是和C语言中的一样,是一个具有function类型的变量。你可以把它作为参数调用另一个函数,比如系统对象Array中的sort方法就是这么做的:
  myArray.sort(orderFunc); // orderFunc为一个比较函数的函数名
也可以把它赋值给其他的变量:
  myFunc2 = myFunc1; // myFunc1为一个已定义的函数
这时对myFunc2的调用与对myFunc1的调用就完全相同了。但是无名函数由于没有名字,所以只能通过把定义式直接赋值给某个变量(如3.1式)来实现其功能(有名函数不能如此赋值)。其他就没有区别了(无名函数同样也可以不赋值给某个变量,不过这样的话就没法调用它了,等于白写)。由此可以看出,FAS中的function类型就相当于C语言中的函数指针类型。

三、自定义函数的运用

  第三点认识:对象的实质是结构体。这点在第一节中就已经提到过了,只不过没有作出具体的解释。熟悉面向对象语言的朋友都知道,对象的方法是在类中预先定义好了的,是定死的,不能更改。但象3.1式那样,FAS中对象的方法却好象是被从外面强加到对象中去的,用的是赋值形式,而且函数体中必须通过this才能访问其对象主体。现在,如果我们把FAS中的对象概念理解为C语言中的结构体,而把对象方法理解为结构体中的函数指针变量,再来看看3.1式,是不是就发现原来所谓的定义方法只不过是在给函数指针变量赋值罢了。再联想一下,是不是又发现其实不用什么无名函数,不用什么.prototype也可以一样定义对象方法的:
  function myMethod() {
    return 1;
  }
  function myObject() {
    this.method = myMethod;
  }
  myobj = new myObject; // 3.2式
  trace(myobj.method()); // 输出 1
不过有一点是特殊的,FAS里的对象可以随时任意添加成员。其实说白了所谓构造函数就是在为一个已定义好的对象添加成员而已。这也就是说象上面这段程序我们可以连构造函数也省了:
  function myMethod() {
    return 1;
  }
  myobj = new Object; // 3.3式
  myobj.method = myMethod;
  trace(myobj.method());
不过在为对象添加成员时必须要保证它已经具有了对象的类型,如上3.3式是不能省略的。当然,我们在构造函数里除了为对象添加成员外还可以对成员赋初值和加入其他的操作,在构造对象时自动执行。从这一意义上讲,定义一个构造函数还是有用的。

三、自定义函数的运用

  第四点认识:谁雇用我,我就叫谁老板。当然这是一种比喻的说法,但却很形象的指出了函数体中this的真正含义。与一般的面向对象语言不同,FAS中的this只指代调用该函数时的实体。这么说可能比较难懂,那就来看一下下面这个例子:
  a = 1;
  function myMethod() {
    trace(this.a);
  }
  function myObject(){
    this.a = 0;
    this.method = myMethod;
  }
  myobj = new myObject;
  myobj.method(); // 3.4式 输出 0
  myFunc = myobj.method;
  myFunc(); // 3.5式 输出 1
这里,3.4式将如一般情况一样输出myobj对象的成员变量a的值0,而3.5式中的myFunc虽然等价于myobj的方法method(即myMethod),但由于这时的调用实体已变成更外层的对象或影片片段,this.a所指的变量也应为此对象或影片片段的成员(即首句中定义的a),因而输出为1。在C++中,this只能指代本对象的实例,也只能在成员函数中使用,而且要把一个成员函数赋值给某个函数指针也是不被允许的。这也充分体现了FAS中其实根本没有成员函数这一概念,所谓“方法”只不过是结构体中函数指针的伪装罢了。再补充一下,this在没有函数调用时永远只指代外层对象或影片片段,所以别指望下面那段程序能为对象myobj添加成员或为成员赋值:
  with (myobj) {
    this.a = 0; // a为要添加的成员
    this.b = 1; // b为myobj的成员
  }

  第五点认识:关于new一个对象的实际流程。大家都清楚,创建一个对象的格式是(没有参数时括号可省):
  myobj = new myObject(...);
可你有没有想过它的实际操作过程呢?别看它长的那么象Java或者C++里的创建对象实例,它可没那么高级,记住了,FAS里的对象实质可是结构体呀!或许你还不知道,在你定义了一个函数的时候,系统就已经为你自动创建了一个同名对象。而它可没有你函数中用this添加的成员,它的唯一成员只有object类型的prototype。现在你有点明白了为什么要有如3.1式那样:
  anyFunction.prototype.anyMember = ...
的写法了吧。以3.2式为例,实际执行创建对象的过程是这样的:
  (1)定义myobj为对象(只是一般的对象,无任何成员)
  (2)执行new后指定的函数(管它是不是构造函数,但若有this则指该对象)
  (3)拷贝myObject.prototype的所有成员到myobj门下
现在你知道FAS中的new与标准面向对象语言中的new的区别了吧,是不是也不用再对prototype产生困惑了呢。

3、函数使用的技巧

录取查询

扫一扫,查询结果

咨询反馈
扫码关注

微信公众号

返回顶部