闭包:定义在一个函数内部的函数被引用到了外部
本质上,闭包是将函数内部和外部连接起来的桥梁。
通常,函数的作用域和它的所有变量都会在函数执行结束后被销毁,但是,在创建了一个闭包以后,这个函数的作用域会一直保存到闭包不存在为止。
这样说可能不理解,让我们看一个经典闭包案例
function test(){
var arr[];
for (var i = 0 ;i < 10; i++)
{arr[i] = function () {document.write(i+" ");} }
return arr;
}
var myArr = test();
for (var j = 0;j < 10;j++)
{
myArr[j]();
}
按照常理应该输出0~9吧,实则不然,居然会输出10个10!
因为函数赋值给数组,和外部函数test形成了闭包。
大家可能会有疑问,为什么不是一一对应的呢?i = 0 时,arr[0] =function() {document.write(0+" ")};这个时候应该打印0啊。
这样想是不对的,我来粗略讲解一下这个过程。
1.首先我们从函数调用执行时开始讲起,因为test函数定义过程所需要用的GO我们用不到。
2.在代码块下面,我们使用var myArr =test();调用函数test,在函数执行的前一刻我们进行预编译,函数test会生成一个AO(不了解这一块可以看看我的另一篇博客,JavaScript作用域),其中AO对象里面存了变量i的值。
3.然后函数开始执行,开始定义了一个arr[]数组,然后通过for循环语句赋值给arr[]数组十个函数,相当于:arr[]={function(){},function(){},function(){},......},数组中被赋予了十个一模一样但彼此独一无二的函数,函数内部是啥不知道,这点很关键,JavaScript中,内部对外部是不可见的,在没有调用function(){}之前,系统也不知道函数内部写了啥,因此,这个函数不会立即执行,函数要在被调用的时候才会执行。
4.最后,return arr;把arr return到了test外部,return语句的执行完毕,意味着test函数执行完毕,然后test的AO会被销毁,等待下一次调用,注意,test的AO销毁的前一刻for循环跑完了吧?跑完了是不是要跳出循环?i等于几跳出循环?10吧,那么test的AO中存的值就是i=10,但是,function是被定义在test内部吧,它会直接引用test的AO,它被return到test函数外部了。
5.于是,当调用myArr这个函数表达式的时候,test的AO是不是还在呢?显然不在了,但是数组中每一个function(){}都拿着test的AO呢,于是数组的每一位打印出来都是10
那么如何解决这个闭包带来的困扰呢?
答:用立即执行函数;
function test(){
var arr[];
for (var i = 0 ;i < 10; i++)
(function (j){
{
arr[i] = function () {document.write(j+" ");
}
(i));
}
return arr;
}
var myArr = test();
for (var j = 0;j < 10;j++)
{
myArr[j]();
}
我在arr外层再加一个立即执行函数,然后将i作为实参,j作为形参。
如此一来,数组arr内部有了十个独一无二的立即执行函数,其内部又有一个function,想一想,每次调用,i会传入立即执行函数的形参j,立即执行函数会产生自身的AO,AO内部保存了j的值,所以即便立即执行函数执行完便销毁,其AO也不存在了,但是数组中的function(){}还拿着立即执行函数的AO呢,它会寻找AO内部的j的值,然后在被调用的时候打印出来,他不会去找test函数的AO,因为作用域链的查找是自顶向下的,它自身的AO有,就不会去父级寻找。
对于这一块,本人自身也不是了解得很深刻很清楚,只是愚见,如有错误,望大佬们指出。