var a = [];
for(var i = 0; i < 10; i++){
a[i] = (function (arg){
return function (){
console.log(arg);
}
})(i);
}
a[5]();//5上面的代码算是一个经典的闭包例子.对于这个闭包的解释是:
带参匿名函数的返回值是一个函数,这个函数生成了独立的作用域.由于这个返回函数内部调用了外部带参函数arg属性,所以带参匿名函数在执行结束后执行环境不会销毁.
问题来了,返回函数并没有被调用过,js解释器是如何知道,返回函数内部调用了带参匿名函数的属性的?哪位大大能从比较底层的角度解释下函数声明的过程(不只是声明提升).个人感觉这一步跟作用域的创建有关,因为执行环境应该是在函数被调用后才初始化的.
return function (){// 这是返回的函数
console.log(arg);
}
})(i);你这匿名函数返回的是一个匿名函数,等于就是你的a数组里最后是5个函数你最后的代码a[5](),这不就是执行函数了吗?等于执行了function(){console.log(arg},那么问题来了,arg在哪?
本函数作用域里木有啊,所以向上找,就找到之前执行的那个匿名函数了,发现这个匿名函数有串进来一个参数叫arg,于是就显示了。
"
你最后的代码a[5](),这不就是执行函数了吗?等于执行了function(){console.log(arg},那么问题来了,arg在哪?
"
我讨论的是在执行:
a[i] = (function (arg){
return function (){
console.log(arg);
}
})(i);
代码时是如何产生闭包的,而不是最后的执行语句.闭包最基本的概念是内部函数引用外部变量导致外部执行环境执行完毕后不会销毁,由此闭包便产生了.我问的便是,内部函数并没有被执行,只是被return语句作为返回值,那么js是如何知道内部函数引用了外部执行环境的变量的?
推荐一篇blog
其中有个机制就是,如果一个数组或对象被引用就无法销毁。
所以你的代码里,所有functionB都被a数组中的元素所引用,所以functionB无法销毁,也就导致了functionA一直无法销毁,所以变量一直存在,所以functionB每次执行都能访问到functionA中的变量。不知道这样你明白木有?
把arg拿掉并不会报错的,因为返回的匿名函数并没有被调用,无论内部声明了什么变量都不影响.只有该函数被调用时才会出现"arg is not defined"异常.还有,关于变量提升.这个操作是发生在执行环境初始化期间,而执行环境初始化是函数在被调用时才会出现的操作,这正好说明了上边的现象.
关于其中的这一段:
"
上面说了,JS里只有块级作用域,一个functionA执行完毕之后,其环境(内部变量啥的)应该被销毁并释放,但是如果返回的是一个functionB,那这个functionB就引用到了functionA的环境,所以导致functionA无法释放,其实哪怕是你不写那个arg变量,外层的functionA一样还是存在的,并没有被销毁。
"
其实这里涉及的一个执行环境的概念,functionB函数被外部引用,所以functionA的执行不会被销毁是对的,
如果不写arg变量,则不会产生闭包.
那么第二次循环时,functionA被再次调用,会重新 产生执行环境将之前的执行环境覆盖掉.