public var array:Array = []
public function setup():void{
for(var i:int=0;i<5;i++){
var obj:Object = {}
obj.num = function():void{
this.n = i
trace(this.n)
}
array.push(obj)
}
}
public function run():void{
for(var j:int=0;j<5;j++){
array[j].num()
}
}
先调用 setup 方法,在 setup 方法的 for 语句中循环创建对象 obj,并为 obj 创建一个方法 num(), 该方法将当前循环的 index 赋值给 obj 的一个属性 n,并 trace 出 n 的值。将 for 循环生成的对象存到数组 array 中用于在 run 方法中取出。
调用 run 方法,取出 array 中的对象,并调用他们的 num() 方法。
猜猜输出的结果是什么?
咳咳….
结果是:
5 5 5 5 5
并不是期望中的(我期望中的):
0 1 2 3 4
为什么呢?
研究并实验了几下才发现,原来函数闭包虽然可以记录上下文环境的 snapshot ,但却是最近状态的 一个 snapshot ,比如上例在 for 循环中,虽然把循环的当前的 i 值赋给了 n ,然而 i 是属于 num() 函数之外的,是 snapshot 中的变量,所以它只记录最近的状态,也就是 for 循环的最后一次,i 等于 5 。输出的就都是 5 了。
按照这个原理,你也可以实验一下,把 i 的最后状态改成其他的值,比如
public function setup():void{
for(var i:int=0;i<5;i++){
var obj:Object = {}
obj.num = function():void{
this.n = i
trace(this.n)
}
array.push(obj)
}
i = 100
}
虽然在 for 循环结束之后才设置的值,不过 i 依然在那个 snapshot 的范围之内,所以输出将会是 5 个 100 。
既然知道了原理,问题解决起来就容易了。只要让 i 不在函数闭包的上下文环境的 snapshot 的范围里就好了。可以这样改写一下:
public var array:Array = []
public function setup():void{
for(var i:int=0;i<5;i++){
array.push(createObj(i))
}
}
public function run():void{
for(var j:int=0;j<5;j++){
array[j].num()
}
}
public function createObj(index:int):Object{
var obj:Object = {}
obj.num = function():void{
this.n = index
trace(this.n)
}
return obj
}
将创建函数闭包的部分拿到 createObj() 方法中,这样这个 snapshot 只是该函数的上下文环境,不会受到 i 的重复赋值的影响,输出结果也是预期的 0 1 2 3 4
Copyright 2007-2008 51AS.com Extended in kingcms 鲁ICP备06001158号