我们先来看一个例子
在JS方法里面设置一个for循环,输出每次循环的值,如下图
我们可以根据闭包的知识来更改一下for循环中的逻辑,利用闭包将i的值传递给a
这次在运行程序我们就可以看到输出内容是0开始输出了
结果如下图
JS中如果for循环中有异步方法,就需要用闭包的方式保留当前循环变量值
学习记录,别提问,我可能答不上来!^-^。。。
1.预编译
我们的需求是希望输出0-9 依次输出 0 1 2 3 4 5 6 7 8 9 ,但是这个输出的是十个10;
为什么是十个10呢,因为test()函数内循环时,arr[i]是被赋值为一个函数体,该函数体并没有执行,所以console.log(i)中的i并没有随着arr[i]中的i一起改变,然后被保存到外部myArr,myArr就是一个数组,里面有10个函数,myArr[j]()就是来调用myArr中的函数运行。当myArr[j]()函数运行console.log(i)时,这个i访问的是test()的AO。
上文说到arr[i]并没有运行,test()中for循环i=10时不满足循环条件,退出循环,这时 test()的AO对象中i就等于10了,在test()函数结束之前,arr被返回到函数外部,这个myArr就继承了test() 的AO对象。至此test()执行完毕,释放AO对象。重点来了,test()的AO 对象释放了,但是!myArr在test()结束之前继承了test()的AO对象,所以test()的AO对象被myArr拿到了手中,没想到吧!!所以在myArr循环输出console.log(i)时,这个i实际输出的是test()AO中的i,这个i在test()循环结束时等于10,所以外部循环调用输出console.log(i),循环10次,输出的就是10个10。好晕!看图:
匿名函数自调会立即执行,让i作为实参,j为形参,函数声明执行前都会预编译生成AO, 形参实参统一,每执行一次形参j都会跟实参 i 改变;循环结束后arr中就相当于放了10个匿名函数自调,并引用了匿名函数自调的AO,每一个AO中j都根据i而累加了,所以在外部调用输出j时,就再这个匿名函数自调中找j,输出就达到我们要的效果啦。下图是arr中匿名函数的AO,每一个j都根据i改变:
至此就完美的解决闭包问题啦!!
学习记录,有写错的地方可以指出改正^-^
1、js没有块级作用域,定义的i变量属于函数n中的变量,在函数n中可以访问到;
2、函数n中主要涉及两个执行环境: arr[]中保存的函数中的局部环境,函数n的局部环境。相应的作用域链为:arr[]中的函数的变量对象->函数n的变量对象。arr[]中的函数在执行时,作用域链向上查找,自身的变量对象中没有i这个变量,继续向上查找函数n的变量对象,而在经历三次循环后,此时i的值已经是3,所以值为3.
3、如果要让i为相应的数值,应该延长作用域链,使用匿名函数构造块级作用域,方法如下:
function n(){var arr = []
for(var i = 0 i < 3 i++){
// 这里通过匿名函数,传入的参数i是传入的值i的一个副本,会把此时i的值保存下来
arr[i] = (function (i) {
return function(){console.log(i)}
})(i)
}
return arr
}
var funs = n()
funs[0]()
funs[1]()
funs[2]()