|
主题: 为了搞活这里的气氛,我想考虑"每天一问"希望大家踊跃回答!
|
网络白痴 | 5D荣誉斑竹
职务:普通成员
等级:4
金币:10.0
发贴:1657
|
#12001/5/24 14:31:56
第一问:什么是自定义函数?它的主要功能是什么?在flash里怎么自定义一个函数?
|
Blackflash.Qi
职务:版主
等级:7
金币:17.0
发贴:6028
|
#22001/5/24 16:10:03
支持
这样可以学习更多的东西
子心你可以负责每天出新的题目
|
网络白痴 | 5D荣誉斑竹
职务:普通成员
等级:4
金币:10.0
发贴:1657
|
#32001/5/24 16:31:59
嗯,没问题,不不不,是有问题,因为总是我问题最多,哈哈!我来负责出题,不过今天的题大家先回答呀!
|
5D荣誉斑竹
职务:普通成员
等级:2
金币:10.0
发贴:585
|
#42001/5/24 23:26:44
function
语法
function functionname ([argument0, argument1,...argumentN]){ statement(s) } function ([argument0, argument1,...argumentN]){ statement(s) } 参数
functionname 新函数的名称.
argument 传送到function零或更多字符串, 数值, 或对象.
statements 为function体设计的零或更多条 ActionScript 语句.
描述
动作; 所设计的完成某些任务的一组语句. 可以在某位置声明, 或定义一个函数, 然后在电影中的不同脚本处调用它. 当定义一个函数时, 也可以为函数指定参数. 参数是函数将要操作的值的占位符. 可以传送给函数不同的参数, 也可以在第次调用它时调用参数.
在函数的statement(s)中使用return动作以使用函数返回或得到一个值.
用法 1: 声明一个带有指定functionname, arguments, 和 statement(s)的函数. 在调用一个函数时, 函数声明也被调用. 允许提前参量; 在同样的动作列表中,函数可以在调用后声明. 函数声明会替换所有相同函数原有的声明. 只要允许, 可以在任何地方使用此语法.
用法 2: 创建一个匿名函数并返回. 此语法在表达式中使用, 并特用于安装对象方法.
播放器
Flash 5 或更高.
例子
(用法 1) 下面的例子定义了函数sqr, 它接受了一个参数, 并返回参数的square(x*x). 注意, 如果在同样的脚本声明和使用函数, 函数声明可能出现在函数的使用之后.
y=sqr(3); function sqr(x) { return x*x; } (用法 2) 下面的函数定义了一个Circle对象:
function Circle(radius) { this.radius = radius; } 下面的函数定义了一个匿名函数, 它计算圆的面积, 并作为Circle对象的一个方法赋予它:
Circle.prototype.area = function () {return Math.PI * this.radius * this.radius}
|
网络白痴 | 5D荣誉斑竹
职务:普通成员
等级:4
金币:10.0
发贴:1657
|
#52001/5/25 8:34:25
好,历害!谢谢!后来者可以多看看多想想!
|
fictiony
职务:普通成员
等级:1
金币:0.0
发贴:13
|
#62001/5/26 0:16:22
以前贴过的冷饭,再拿来炒一炒,嘻嘻^v^
三、自定义函数的运用
加入函数这一概念可能是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、函数使用的技巧
首先要讲到的是有关参数的运用技巧。在FAS中,向函数传递的参数都是传值型的,即有形参与实参的区别。在函数体内对实参本身进行修改不会影响到实参的数值。然而遗憾的是,FAS并不提供对指针变量或引用变量的支持,所以象交换两个变量的值这种最简单的函数功能就不能直接得到实现。所幸的是,FAS有特殊的目标路径语法和可谓“万能”的eval函数,为这一不足提供了弥补的途径。让我们看看用它是如何实现交换两个变量的值的: function swapVariables (var1_name, var2_name) { var temp = eval(var1_name); eval(var1_name) = eval(var2_name); eval(var2_name) = temp; } a = 1; b = 2; swapVariables("a","b"); trace(a); // 输出 2 trace(b); // 输出 1 这里,函数swapVariables的参数必须是要交换的两个变量的名称(字符串),这样在函数体内才能通过eval函数找到它们的实体。另外,临时变量(例子中为temp)绝对不能与参数变量(例子中为a和b)的名称相同,否则eval函数返回的点语法对象将与临时变量产生混淆而导致错误的结果。为了避免这种情况,在传递实参时可以加以路径(一般用this)描述: swapVariables("this.a","this.b"); 由于路径的作用,函数体内的临时变量一般就不可能与参数同名了,也就避免了混淆。
FAS中的自定义函数参数灵活性很大。你在调用时可以随便多加或少加,而且类型也可以任意。系统会自动将多余的参数忽略,不足的参数则作未定义处理。这样,你就可以在函数体内通过对参数类型和是否定义过的判断来作不同的处理了。比如: function listProperties (obj, repeat_times) { var times, name, show_string; if (typeof(repeat_times)=="undefined") times = 1; else times = Number(repeat_times); for (var i=0; i if (typeof(obj)=="object") { show_string = "Properties:"; for (name in obj) { show_string += " "+name+":"+obj[name]; } trace(show_string); } else { trace("Not an object!"); } } } mybook = {Name:"English",PageNum:312}; a = 1; listProperties(mybook); listProperties(a,3); 输出: Properties: Name:English PageNum:312 Not an object! Not an object! Not an object!
其次要讲到的就是局部作用域的问题。前面已经提到过,用var可以声明一个局部变量,同时也强调了它只对函数体有效。这里除了重申这一点外,还要补充两点:在影片片段里定义的变量也都是局部变量,它的存活周期就是该影片片段的存活周期;函数的形参也是局部变量,其存活周期与函数内用var定义的局部变量相同,都是该函数体,例子如下: a = 1; function myFunction (a) { a = 2; trace(a); // 输出 2 } myFunction(a); trace(a); // 输出 1
最后要讲的是关于对象成员函数的分配优化问题。前面已经提过,给对象加成员函数总共有三种方式: (1)单独定义一个函数,然后把函数指针赋给对象成员,如: function myMethod() { ... } function myObject() { ... this.method = myMethod; } (2)单独定义一个函数,然后把函数指针赋给初始化函数的prototype对象,如: function myMethod() { ... } function myObject() { ... } myObject.prototype.method = myMethod; (3)直接给初始化函数的prototype对象的成员加函数,如: function myObject() { ... } myObject.prototype.method = function () { ... } (4)直接给对象的成员加函数,如: function myObject() { ... this.method = function () { ... } } 其中,第(4)种方式不被推荐,因为它会在每个对象实例中都载入该成员函数的实体。而其他三种方式都只是在对象实例中加入了该函数的指针而已,从而节省了内存的开销。
|
流浪的牧师
职务:普通成员
等级:7
金币:23.0
发贴:7973
|
#72001/5/26 21:03:05
子心老弟在这混呢。好主意,以后到我的地盘也搞搞这个吧!
|