如何解决 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访问查询字符串参数