《AS3 Expert》动态语言的基石:函数闭包

《AS3 Expert》动态语言的基石:函数闭包


Admin
2010年6月19日名人名言:时间,每天得到的都是二十四小时,可是一天的时间给勤勉的人带来智慧和力量,给懒散的人只留下一片悔恨。——鲁迅

 原文:http://sban.biz/216


闭包(Closure)是函数(或方法)及其执行环境的组合体,它不仅包括函数(或方法)本身,也包括函数(或方法)运行时的上下文词汇环境。闭包是所有动态语言的基石,闭包实现了函数(或方法)可以作为参数传递给函数(或方法)。


 


1,用一个代码实验例示闭包概念


在AS3中,共种三种闭包:


1)函数闭包(Function Closure)


2)方法闭包(Method Closure)


3)类闭包(Class Closure)


public class Closure extends Sprite
{
public function Closure()
{
super();
init();
}

private var author :String = "sban";
private var onMouseClick2 :Function = function(e :MouseEvent) : void
{
trace("author:" + author, "this:" + this);//author:undefined this:[object global]
};

private function init() :void
{
var onMouseClick1 : Function = function(e :MouseEvent) : void
{
trace("author:" + author, "this:" + this);//author:sban this:[object global]
};
this.stage.addEventListener(MouseEvent.CLICK, onMouseClick2);
}

private function onMouseClick(e :MouseEvent) :void
{
trace("author:" + author, "this:" + this);//author:sban this:[object Closure]
}

}

 


在上例代码中,分别以onMouseClick,onMouseClick1,onMouseClick2为listener向stage添加click事件监听,trace结果如代码中注释。其中,onMouseClick是方法闭包,onMouseClick1与onMouseClick2是函数闭包。这里有一个问题,为什么在onMouseClick2中author的输出结果是undefined?


2,三类闭包的区分


在AS3中,任何一个函数(或方法)调用,至少会有一个this参数,这几乎是所有动态语言一惯的规则,不同的是,有的语言对程序员是可见的,如Python,有的则不可见,如AS3。


1) 方法闭包


所有类实例的方法,作为参数传递时,均是方法闭包,隐匿的第一个this参数永远是类实例本身,如上例代码中的onMouseClick便是方法闭包,所以它的this输出为[object Closure]。


2) 函数闭包


所有匿名方法(包括局部变量方法,类变量方法,见上),全局方法(包括位于根包下的全局方法,位于子包下的全局方法,见下)均是函数闭包,所有函数闭包的第一个函数如果是null,将被默认替换为Global对象,所以我们看到的输出均是[object global]


package
{
import flash.events.MouseEvent;

function onMouseClick3(e :MouseEvent) :void
{
trace("this:" + this);//this:[object global]
}
}

package sban.as3Expert
{
import flash.events.MouseEvent;

public function onMouseClick4(e :MouseEvent)
{
trace("this:" + this);//this:[object global]
}
}

 


对于位于子包下的函数,可以这样直接使用:


this.stage.addEventListener(MouseEvent.CLICK, sban.as3Expert.onMouseClick4);

3)类闭包


这是三类闭包中最简单的一种,也是最容易区分的一种,可能也是价值最低的一种,貌似根本不应该归为闭包类别。在AS3中,所有显式对象类型转换均是类闭包,如下:


//if obj is Closure which type anotation is Object
var obj1 :Closure = Closure(obj);

Closure在这里不是操作符,也不是别的什么东西,在这里应该把它理解为一个特殊的方法。这个方法第一个参数仍然为this,第二个参数是将被作类型转换的对象,在上例代码中为obj。


3,改变函数闭包的this参数的一种情况


在函数闭包中,this参数(null)通常被默默置换为global对象,在某些情况下,程序员可以传递真实的this参数进去,而不是null,从而避免被替换为global对象。


在Array的forEach, every, map, some, filter这些API中,第一个参数为函数对象,第二个参数为第一个参数的this对象,当程序员指定第二个参数时,便可以在第一个函数内访问this上下文环境的变量,如果不指定,便不能再其内使用this。


public function ArrayForEachThis()
{
super();

var arr :Array = [1,2,3];
arr.forEach(
function (item :int,index :int=-1,arr :Array = null) :void
{
trace(item, this.author)
}
//,this
);
}

private var author :String = "sban";

编码规范:在使用Array的forEach, every, map, some, filter这些API时,必须在第二个参数位传递this进去。


4,为什么在onMouseClick2中author的输出是undefined?


所有AS3程序员都应当知道,在AS3运行时,有一个作用域链,该作用域链自global始,在运行时变量首先从最近的链点查找,如果未找到,再向上查找,直到找到或到达global链点。


onMouseClick2函数的运行时作用域链为:


onMouseClick2闭包->global

在这个链条内,根本不存在author变量,所以onMouseClick2的输出为undefined。


而onMouseClick1的作用域链为:


onMouseClick1->init->Closure->global

onMouseClick的作用域链为:


onMouseClick->Closure->global

这两个作用域链均包括author变量,所以onMouseClick1与onMouseClick均不会输出undefined。


作用域链的节点包括闭包对象,但不仅包括它。


sban 2009/5/15 北京。转载请注明作者及出处,非商用。



    • E4X用法简要

    • 命名空间:如何控制未知方法的调用

    • 变量及作用域

    • Traits对象:为什么静态方法不能被继承

    • 像Ruby一样简易行码:使用Prototype扩展原生对象

    • Flash Player的垃圾内存回收机制:能否强制回收?

    • AS3中的一些基本定义,Function与Method的区别

    • AS3中的八种基元类型

    • 在AS3中,如何实现数组及对象的深拷贝?

    • 如何让if语言写的更简洁?(七种布尔转换为假的情况总结)


作者:sban

出处:http://sban.biz


本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。



时间,每天得到的都是二十四小时,可是一天的时间给勤勉的人带来智慧和力量,给懒散的人只留下一片悔恨。——鲁迅
来源:http://www.cnblogs.com/sban/archive/2010/05/27/1745357.html
 
 
补充可有意思的例子:
package

{

 import flash.display.Sprite;

 import flash.events.MouseEvent;

 

 public class Main extends Sprite

 {

  public function Main()

  {

   //func1(2);

   //func1(3);

   var b:int=1;

   func1(b);

   b++;

   func1(b);

   

  }

  public function func1(a:int):void

  {

   stage.addEventListener(MouseEvent.CLICK,func2);

    function func2(e:MouseEvent):void

   {

    trace(a);

   }

   return;

   

  }

  

  

  

 }

}

在场景上按一下,结果:

1

2

充分说明了运行时保存上下文环境的特性。

你可能感兴趣的:(动态语言)