Flash ActionScript3 殿堂之路第四章--4.4 *函数高级使用技巧

来源:互联网 发布:现金管理优化方案设计 编辑:程序博客网 时间:2024/05/29 04:19

4.4 *函数高级使用技巧
      知道函数本身也是一种对象,会给我带来极大的便利和编程思维的改变。下面共享几个笔者使用的技巧。

4.4.1技巧一:代理函数对象
       这是最简单的运用。设立一个代理函数对象,根据条件的不同,将它指向不同的函数,可实现动态改变(即运行时改变)。相信有经验的程序员都了解动态改变函数的便利性。而且在于ActionScript提供了这种便利,运用这个特性可以衍生大量技巧。见示例4-4。

 

  1. var kingdaFunc:Function;
  2. var sex:String = “male”;
  3. if (sex == “male”) {

  4. kingdaFunc = maleFunc;
  5. } else {

  6. kingdaFunc = famaleFunc;
  7. }
  8. kingdaFunc();
  9. //输出:I am a boy
  10. function maleFunc() {

  11. trace (“I am a boy”);
  12. }
  13. function femaleFunc() {

  14. trace (“I am a girl”);
  15. }
复制代码

 

4.4.2技巧二:建立函数执行队列
比如说,我有一个对象,我想根据不同的情况对它进行一系列的操作。但是有时需要所有的操作,有时有只需要一部分的操作。那么这个较高级的技巧,就能保证代码的高度重用性和简捷。见示例4-5。

示例4-5 建立函数执行队列

 

  1. var funcAry:Array = new Array();

  2. //将需要的操作步骤加入队列
  3. funcAry.push(aFunc);
  4. funcAry.push(bFunc);
  5. funcAry.push(cFunc);


  6. //供操作的对象
  7. var originObject:Object = new Object();

  8. //需要执行几步由execQueue这个参数决定,在实际工程运用中这个数可能是动态决定的
  9. var execQueue:Number = funcAry.length;

  10. //核心步骤:函数队列执行。实际运用中可以把它包装成一个函数,或者一个类的实例
  11. for (var i:Number =0; i<funcAry.length; i++) {
  12. funcAry (originObject);
  13. }

  14. //trace出执行操作后的originObject里面的内容
  15. for (var j in originObject) {

  16. trace (j + “:” + originObject[j]);
  17. }

  18. //操作步骤a,b,c
  19. function aFunc(eO:Object) {

  20. e0.aFuncExected = true;

  21. trace (“aFunc()”);
  22. }
  23. function bFunc(eO:Object) {

  24. eO.aFuncExected = true;

  25. trace (“bFunc()”);
  26. }
  27. function bFunc(eO:Object) {

  28. eO.aFuncExected = true;

  29. trace (“cFunc()”);
  30. }
复制代码

 

输出内容为:

 

  1. aFunc()
  2. bFunc()
  3. cFunc()
  4. aFuncExected:true
  5. bFuncExected:true
  6. cFuncExected:true
复制代码

 

前3行表明a、b、c3个函数按顺序执行了。后3行表明orginObject确定经过了3步操作,多了3个为true的属性。
    笔者提醒:技巧可以再延伸!可以通过一个函数来管理队列里面各个元素的位置,达到改变操作函数的顺序。比如,通过一个数组来安排调用顺序。以下代码,紧接着上文的代码例子。

 

  1. var operationAry:Array = [2,1,0];
  2. for (var k:Number = 0; k<operationAry.length;k++) {
  3.   funcAry[operationAry[k]](originObject);
  4. }
  5. /*输出:
  6. cFunc()
  7. bFunc()
  8. aFunc()
  9. */
复制代码

 

这样函数就通过2、1、0这样的倒序来执行操作。
这个技巧还有很多可以延伸的地方,比如说动态控制操作函数的参数多等,甚至可以通过外部数据(如XML)运行时改变函数执行顺序。大家可以自己研究扩展。
4.4.3技巧三:利用函数返回
      函数有一个proxyObject对象,我们希望根据proxy对象的内容来确定一个方法,处理myObject对象。
      当proxyObject是字符串时,我们又希望根据字符串的内容来确定返回不同的函数(或方法)。这些函数参数和类型是不完全相同的。有的可能是一个参数,有的可能是多个参数,不同类型。
      那么传统的解决方法——在函数内部调用其他函数——就显得力不从心。即使解决,也不如笔者下面这个chooseFuncBy()函数简捷。缺点当然有,那就是比较灵活,使用者一定要清楚地管理好每个目标函数和判断逻辑。因为这种灵活的编程方式编译器无法检查,是不能查出类型不匹配这种错误的。初学者慎用。见示例4-6。

示例4-6 利用函数返回函数

 

  1. //通过A调用只有一个参数的aFunc();
  2. chooseFuncBy (“A”) (“A func has only one parameter.”);
  3. //输出:aFunc(): A func has only one parameter.

  4. //通过B调用有两个参数的bFunc();
  5. chooseFuncBy(“B”) (“B func has two parameters.”,”No.2 parameter”);
  6. //输出:bFunc():B func has two parameters.one more Parameter:No.2 parameter

  7. //字符串不符,默认函数
  8. chooseFuncBy(“wu lala”)(“I choose A function”);
  9. //输出:Welcome to Kingda.org!My blog

  10. var withObj:Object = new Object();
  11. var myObj:Object = 
  12. {name:”黑羽”,blog:”http://www.kingda.org”, hobby:”Starcraft”};
  13. chooseFuncBy(withObj)(myObj);
  14. /*输出:
  15. objectFunc();
  16. name:黑羽
  17. blog:http://www.kingda.org
  18. hobby:Strarcraft
  19. */

  20. function chooseFuncBy(input:*):Function {

  21. //运用一:利用参数的种类来确定返回的函数
  22. if (!(input is String)) {

  23. return objectFunc;
  24. }

  25. //运用二:根据参数内容来返回函数
  26. switch (input) {
  27. case “A”:
  28. return aFunc;
  29. case “B”:
  30. return bFunc;
  31. default:
  32. return kingdaFunc;
  33. }
  34. //…更多延伸运用:利用参数个数、is确定不同Class的实例来选择函数,等等
  35. }
复制代码

 

 

  1. function aFunc(nS:String) :void {

  2. trace (“aFunc():” + nS);
  3. }

  4. function bFunc(nS:String, nP:String) :void {

  5. trace (“bFunc():” + nS + “one more Parameter:”+ nP);
  6. }

  7. function kingdaFunc(…rest) :void {


  8. trace (“Welcome to Kingda.org! My blog”);
  9. }

  10. function objectFunc(kingdaObj:Object) :void {

  11. trace (“objectFunc():”);

  12. for (var i in kingdaObj) {

  13. trace (i + “:” + kingdaObj);
  14. }
  15. }
复制代码

 

4.4.4技巧四:函数动态添加实例属性

    如4.3节所说,使用函数表达式定义的函数也是一个动态类对象。因此,使用函数表达式定义的函数可以动态添加属性和方法。
    注意,技巧四和技巧五完全可以用面向对象的方法来解决。从面向对象的角度来说,频繁使用技巧四和技巧五不是一个好的编程习惯。虽然很方便,但由于动态属性的使用使得编译器无法进行类型检查,也使得在复杂代码中的犯错可能性增加,查错难度增大。技巧四和技巧五只是运用在一些特殊的场合,在面向过程的编程中使用稍多一些。
下面来介绍技巧的运用。
    例如,利用函数动态属性来计算函数调用次数。当然,你完全可以用这个技巧来干更多更有用的事,笔者只是抛砖引玉做个小例子。
一个游戏中有开火函数shot()。我想知道总共开火了多少次,那么可以添加一个shot函数的动态属性times。注意,引用shot函数动态属性时,只能使用“函数名+数组访问符+属性名”来访问,例:shot[‘times’]。见示例4-7。

示例4-7函数动态添加实例属性

 

  1. var shot:Function = function ():void {

  2. shot[‘times’] ++;

  3. trace (“Shot():times:”+ shot[‘times’]);

  4. //.. 可以写些其他代码放在这儿
  5. }

  6. shot[‘times’] = 0;
  7. //初始化times
  8. shot();
  9. //输出:Shot():times:1
  10. shot();
  11. //输出:Shot():times:2
  12. shot();
  13. //输出:Shot():times:3
复制代码

 

4.4.5技巧五:函数对象动态添加实例方法
      技巧五就是添加方法了,更有趣一些。函数这么一摆弄之后,函数对象成了“二不像”:不像普通类实例,也不像一个函数。这个技巧可以让我们的函数变得很强大,也会让它更复杂更难以管理。本技巧即使在面向过程编程中,也只建议在小范围内少了运用,不赞成大范围大规模地使用。
见示例4-8,本例中一旦发现shot()函数开火超过3次,就会自动调reload,将开火次数重置为0。

示例4-8 函数对象动态加实例方法

 

  1. var shot:Function = function():void {

  2. shot[‘times’] ++;

  3. trace (“Shot():times:”+ shot[‘times’]);

  4. shot[‘reload’]();
  5. //shot的其他代码放在这儿
  6. }
  7. shot[‘times’] = 0;

  8. shot[‘reload’] = function () {

  9. trace (“reload:”+ this[‘times’]);

  10. if (this[‘times’] >3) {

  11. this[‘times’] = 0;
  12. }
  13. }

  14. shot[‘reload’]();
  15. shot();
  16. shot();
  17. shot();
  18. shot();
复制代码

 

 

  1. shot();
  2. shot();

  3. /*输出:
  4. reload:0
  5. Shot():times:1
  6. reload:1
  7. Shot():times:2
  8. reload:2
  9. Shot():times:3
  10. reload:3
  11. Shot():times:4
  12. reload:4
  13. Shot():times:1
  14. reload:1
  15. Shot():times:2
  16. reload:2
  17. */
复制代码

 

写在篇尾的话:可以看出,运用动态添加属性和方法的技巧,可以使Function这个特殊的东西异常强大。而且其灵活程度更是空前,试想如果动态添加的方法返回函数(见4.4.1节第一个技巧)。不要忘了,动态添加的方法可直接访问函数的输入参数,那么其衍生的技巧又有多少种呢?函数又可以变成怎样一种强有力的编程对象呢?只有想不到,没有做不到。这就是Function给我们展示的无穷灵活性。
        但是,还是要说的是,技巧终归是技巧,它有其两面性。灵活是它的优点,也是它的缺点。小范围地运用让你爽快无比;大项目中大范围地使用,除非管理得很好,不然会让你头疼欲裂死而后快。
         4.5 本章小结函数是面向过程编程的核心元素,也是遥望面向对象编程殿堂的第一个界碑。其重要性不言而喻。
         本章讲解了ActionScript3中函数的两种定义法方法:语句定义法和表达式定义法,并花大篇幅详细地说明了二者的不同。并在函数本质一节中,点出了二者在底层实现上的不同。本章还介绍了ActionScript3中新增的函数特色、默认参数和…(rest)关键字。
在函数高级使用技巧中,介绍了ActionScript3函数特有的使用技巧。我们会发现随着不断挖掘函数自身的潜力,在慢慢地开始向面向对象思想靠拢。

 

原创粉丝点击