如何解决 ActionScript 3 (AS3) 中的关闭问题

Posted

技术标签:

【中文标题】如何解决 ActionScript 3 (AS3) 中的关闭问题【英文标题】:How to fix closure problem in ActionScript 3 (AS3) 【发布时间】:2010-09-30 04:23:26 【问题描述】:

在下面的代码中,我尝试加载一些图像,并在它们单独加载后立即将它们放入舞台。但它是错误的,因为只显示最后一张图像。我怀疑这是一个关闭问题。我该如何解决? AS3 中闭包的行为与 Java Script 中的行为不一样吗?

var imageList:Array = new Array();
imageList.push('src':'image1.jpg');
imageList.push('src':'image2.jpg');
var imagePanel:MovieClip = new MovieClip();
this.addChildAt(imagePanel, 0);

for (var i in imageList) 
    var imageData = imageList[i];
    imageData.loader = new Loader();

    imageData.loader.contentLoaderInfo.addEventListener(
        Event.COMPLETE, 
        function() 
            imagePanel.addChild(imageData.loader.content as Bitmap);
            trace('Completed: ' + imageData.src);             
        );

    trace('Starting: ' + imageData.src);
    imageData.loader.load(new URLRequest(imageData.src));   

【问题讨论】:

我不建议使用 javascript 标签,因为它没有太大的相关性,所以我删除了它。 【参考方案1】:

AS3 中的闭包行为与 Java Script 中的行为不一样吗?

是的,JavaScript 做同样的事情。和 Python 一样。和其他人。

虽然您在“for”中定义了“var imageData”,但 for 循环不会在这些语言中引入新的范围;实际上,变量 imageData 绑定在包含范围内(外部函数,或者在这种情况下,它似乎是全局范围)。您可以通过在循环完成执行后查看 imageData 并在其中找到 imageList 的最后一个元素来验证这一点。

所以只有一个 imageData 变量,而不是循环的每次迭代。当 COMPLETE 触发时,它进入闭包并读取 imageData now 的任何值,而不是在定义函数时 (*)。通常,for 循环将在 COMPLETE 触发点完成,并且 imageData 将保存最后一次迭代中的最后一个元素。

(* - 存在“早期绑定”语言,在您定义闭包时评估变量的值。但 ActionScript 不是其中之一。)

可能的解决方案往往涉及使用外部函数来引入新范围。例如:

function makeCallback(imageData)  return function() 
    imagePanel.addChild(imageData.loader.content as Bitmap);
    trace('Completed: ' + imageData.src);                                                                                                     
 
...
imageData.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, makeCallback(imageData));

你/可以/把这个内联,但是双重嵌套的 function() 开始变得难以阅读。

另请参阅 Function.bind() 以获得可用于实现此目的的通用部分函数应用程序功能。它很可能成为未来 JavaScript/ActionScript 版本的一部分,同时可以通过原型设计添加到语言中。

【讨论】:

感谢您解决问题。所以实际上它的行为与 Java Script 不同,因为 Java Script 是一种“早期绑定”语言,在我看来它更好。为什么更喜欢 AS3 行为? 不,ActionScript 和 JavaScript 的行为相同 - 它们必须如此,因为它们都符合 ECMAScript 标准。早期绑定语言也不是,它们往往在函数式编程中出现更多。我希望看到一种基于现代脚本语言的早期绑定语言。 你是对的!我运行了一些测试,实际上行为是相同的。我想我认为这是不同的,因为我从来没有遇到过这种 Javascript 延迟的情况。谢谢! JQuery 会以某种方式改变这种行为吗?我经常使用它,也许它让我感到困惑。 不是天生的,不是,而是jQuery的sequence.Each()在每次迭代时调用一个函数,每次都重新绑定函数变量。所以它避免了 for/while 循环可能遭受的问题。【参考方案2】:
(function() 
  var imageData = imageList[i];
  imageData.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function() 
    // use imageData;
  );
).apply();

【讨论】:

【参考方案3】:

在 Array 类上使用更实用的 forEach 方法可以避免这个问题。 这已经提到过,但我会在这里展开。

imageList.forEach( function ( item:MovieClip, index:int, list:Array) 
    // add your listener with closure here
)

使用此方法,传递给 forEach 的函数在每次迭代时定义一个新范围。现在您可以在此范围内添加一个闭包,它会根据需要记住每个实例。

关于相关说明:

一直输入这 3 个参数很痛苦,所以... 您还可以使用适配器功能使其变得更少/更难看:

// just give me the item please
imageList.forEach ( itrAdpt( function ( image: ImageData ) 
    // add your listener with closure here
))

// give me the item and it's index
imageList.forEach ( itrAdpt( function ( image: ImageData, index:int ) 
    // add your listener with closure here
))

// give me the item, index and total list length
imageList.forEach ( itrAdpt( function ( image: ImageData, index:int, length:int ) 
    // add your listener with closure here
))

itrAdpt 是一个(可能是全局的)函数,定义如下:

public function itrAdpt(f: Function): Function

    var argAmount:int = f.length

    if (argAmount == 0)
    
        return function (element:*, index:int, colection:*):* 
            return f(element)
        
    
    else if (argAmount == 1)
    
        return function (element:*, index:int, colection:*):* 
            return f(element)
        
    
    else if (argAmount == 2)
    
        return function (element:*, index:int, colection:*):* 
            return f(element, index)
        
    
    else if (argAmount == 3)
    
        return function (element:*, index:int, colection:*):* 
            return f(element, index, colection.length)
        
    
    else
    
        throw new Error("Don't know what to do with "+argAmount+"arguments. Supplied function should have between 1-3 arguments")
    

【讨论】:

【参考方案4】:

如果创建一个命名函数对您没有吸引力,bobince 的答案可以转换为这个,而不会牺牲太多可读性:

var makeCallback = function(imageData:String) 
   
    return function(evt:Event) 
    
      imagePanel.addChild(imageData.loader.content as Bitmap);
      trace('Completed: ' +  imageData.src);
     
  

...

imageData.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, makeCallback(imageData));

只是我的喜好,您的里程可能会有所不同。

【讨论】:

以上是关于如何解决 ActionScript 3 (AS3) 中的关闭问题的主要内容,如果未能解决你的问题,请参考以下文章

ActionScript 3 AS3:弹出窗口拦截器解决方法

ActionScript 3 使用TLFTextField时AS3 Bug访问FlashVars(解决方法)

ActionScript 3 AS3 getURL / navigateToURL(如何在AS3中创建可点击链接)

ActionScript 3 如何使用AS3访问查询字符串参数

ActionScript 3 AS3 - 如何从另一个类访问文档类

ActionScript 3 AS3 SWFtrospection(as3swf)