As语言闭包

来源:互联网 发布:ubuntu精简版下载 编辑:程序博客网 时间:2024/06/08 00:19
翻译如下:
闭包是动态语言的基础。实现类似面向对象的特征或搭建框架,它们具有重要的作用。但是,对闭包一个普通的定义并不能帮助我们理解它。让我们通过一些例子来进行。首先,我会举例说明闭包是什么样的,然后介绍它的使用规则。
首先假设存在这样一个匿名函数,它访问函数外的变量

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="doInit(event)">
    <mx:Script >
        <![CDATA[
            import mx.controls.Alert;
            private var greeting: String = "Hello";
            private function doInit(evt: Event): void {
                btn.addEventListener(MouseEvent.CLICK, function(evt: Event): void {
                    Alert.show(greeting + "," + txt.text);
                });
            }
        ]]>
    </mx:Script>
    <mx:Button id="btn" label="closure" />
    <mx:TextInput id="txt" />
</mx:Application>

编译运行上面的代码
以下是关于闭包的描述,非常简单,分为3个部分
1. 闭包是被定义在类或者上下文中的方法,并且一段时间后被传递到另一个对象中执行
2. 闭包调用发生在运行时(可以在程序运行过程中被多次调用)。它是存储所有将被使用的上下文变量(以上实例中的"greeting")的堆栈帧的位子。一般来说,它是描述主程序方法中的变量和当前运行时的类实例。
3. 最后,闭包的执行可以发生在任何时候,并且在被调用时带有参数。通常情况下调用者会把信息包含在一个"Event"对象中传递进来

看起来你可以使用闭包给参数来个"快照"。不幸的是,对于某些动态语言它是可以的,但是作为ECMA标准下的ActionScriot和 JavaScript除外。接下来通过一些例子来解释为什么这样。首先,我们要明确ActionScript闭包是运行时的,而不是动态解释的。我们先调换一下闭包和greetings定义的顺序。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="doInit(event)">
    <mx:Script >
        <![CDATA[
            import mx.controls.Alert;
           
           
            private var myClose: Function = function(evt: Event): void {
                Alert.show(greeting + "," + txt.text);
            };
            private function doInit(evt: Event): void {
                btn.addEventListener(MouseEvent.CLICK, myClose);
            }
           
            private var greeting: String = "Hello";
        ]]>
    </mx:Script>
    <mx:Button id="btn" label="closure" />
    <mx:TextInput id="txt" />
</mx:Application>

它依然正常,尽管"greeting"在闭包被定义的时候为undefined--以上证明了闭包仅仅使用的是引用。不像java,一个对象的域是一个封闭函数或类的堆栈帧内容。以下是一个例子,在java中是编译不通过的,但是在As3中却是非常合理:

private function aaa(): void {
    {var a = 1;} //在java中a在括号外面是不可见的
    Alert.show(a);
}

Flash 是个堆栈机器,在一个封闭的函数当中,一个闭包是一个堆栈变量。这种堆栈帧的方式极大的简化了基于堆栈的闭包和代码优化的实现,尽管在编码的时候它需要一 些判断。另一点是,在这里我们不需要对象值,仅仅是引用。让我们在greeting被调用前,将它设置成某个值。
<mx:Button label="closure" click="greeting='good morning'"/>
正如你所看到的,提示框上显示的是一个新的值。"闭包"不会使用greeting在被定义时的值。
闭包是ActionScript中的一等公民。在你的类中,所有方法都是闭包。这就是为什么方法能知道类中所有的实例变量。所有类本质上都是一个大闭包。你可以写一个包含闭包的函数来
像类一样的实现需求。
当你使用异步操作或者向另外一个对象传递事件时, 使用闭包是不可避免的。flex与服务器比较重要的交互动作,或者获取用户的输入,他们基本上都是异步的。使用闭包你可以获得类实例的引用,在类实例中的 函数完成对外部对象的事件处理。在大部分情况下,这样足以处理异步方法的结果。通过this来引用定义了方法的上下文,极大的简化了编码,因为对于开发人 员显得更加自然。
早在flex2之前,flex1.5的时候,开发人员需要为闭包设定好上下文。改变闭包上下文的能力,给实现真正的动态提供了很大的灵活性。
接下来的代码例子描述了在任意对象上的一个闭包可以提供一个用户上下文对象:

public class Closure    {
        public function Closure()
        {
        }
       
        public static function create(context: Object, func: Function,...pms): Function {
            var f: Function = function(): * {
                var target: * = arguments.callee.target;
                var func: * = arguments.callee.func;
                var params: * = arguments.callee.params;
                var len: Number = arguments.length;
                var args: Array = new Array(len);
                for(var i: Number = 0; i < len; i++)
                    args[i] = arguments[i];
                args["push"].apply(args, params);
                return func.apply(target,args);
            };
            var _f: Object = f;
            _f.target = context;
            _f.func = func;
            _f.params = pms;
            return f;
        }

    }

以下代码解释如何调用这个闭包

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="onCreationComplete(event)">
    <mx:Script >
        <![CDATA[
            import closure_test.Closure;
            import mx.controls.Alert;
            private var myClosure: Function;
           
            private function onCreationComplete(evt: Event): void {
                myClosure = Closure.create({greeting: "Good evening"}, function(name: String): void {
                    Alert.show(this.greeting + "," + name);
                },"world");
                var greeting: String;
                greeting = "Hello";
            }
           
        ]]>
    </mx:Script>
    <mx:Button id="btn" label="closure" click="myClosure()"/>
    <mx:TextInput id="txt" />
</mx:Application>

提示显示"Good evening, world" 因为方法调用时使用了不同的上下文。这个方法一般被叫做"委托",被事务框架用于集中处理特定的事件。
上面的例子阐述了上下文,方法和闭包之间的关系。使用这个技术,你可以实现动态继承,多态性和其他面向对象概念
//=====================================Over==================================

这里说说个人的理解:首先,在as3中,函数分为函数表达式和函数语句,函数表达式的定义是
function funName(...): void {},而函数语句是 var funName: Function = function(...): xx {},文章里说过,一个类中的成员函数,甚至整个类都是闭包。那么闭包是什么呢,个人理解,它是一个具有独立上下文的函数。在Function类中有一个 apply的方法,而它需要的参数,一个是thisObject:Object,一个是argArray:Array。前者是函数的调用对象,也就是给 Fcuntion创造上下文的,可以通过this关键字来获得属性值。后者是传递给Function的参数。 所以,一个类中的函数方法,就是具有了上下文的闭包。至于Class这个类概念,还不知道编译器是怎么实现的(class关键字在as1.0版本是没有 的,是2.0为了实现类型继承才增加的)。
上文的最后一段代码,比较难看懂,它通过一个静态类方法创建出一个闭包。注意 Alert.show(this.greeting + "," + name); 这里的this关键字,因为闭包是运行时的,所以this上下文是在运行时确定的,去掉this的话,就直接引用到外面的greeting了。
下面修改一下代码,来阐述一下apply的用法和函数的概念

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
    <mx:Script >
        <![CDATA[
            import closure_test.Closure;
            import mx.controls.Alert;
            private var myClosure: Function;
                 
            private var greeting: String = "Hello";
            //============情况1=========== output: Hello, world
            private var func: Function = function(name: String): void {
                Alert.show(this.greeting + "," + name);
            }
            //=============情况2=========== output: good evening, world
            /* private function func(name: String): void {
                Alert.show(this.greeting + "," + name);
            } */
        ]]>
    </mx:Script>
    <mx:Button id="btn" label="closure" click="func.apply({greeting: 'good evening'}, ['world'])"/>
    <mx:TextInput id="txt" />
</mx:Application>

上面情况1和情况2的区别是函数方法和函数语句的区别,在类中声明一个函数的时候,它的上下文已经定了,所以this.greeting是Hello。还要注意去掉this关键字的区别
原创粉丝点击