-
什么是闭包
- 闭包是指 一个函数嵌套一个内部函数,这个内部函数可以在外部函数体外调用外部函数内的变量 。
通俗的说就是一个原来在函数a体内的函数b被调用在函数a体外执行并且b函数还可以调用a函数体内的变量。
- 闭包是指 一个函数嵌套一个内部函数,这个内部函数可以在外部函数体外调用外部函数内的变量 。
-
一般函数与闭包函数的区别:
- 一般函数的内部函数如果在其嵌套的函数的函数体外运行并调用嵌套函数的变量是不被允许的。
因为此时的内部函数不再是嵌套函数的局部变量可以随意使用大作用域下的变量,而是和嵌套函数同级别的变量,无法访问其原嵌套函数内部的私有变量。
-
普通外部函数运行后产生的作用域就会被GC回收机制回收,但是闭包函数不会回收自身的作用域内存也就是AO对象。这就会造成内存泄漏也就是被AO对象占用的内存一直无法被回收。
因为内部函数在外部引用了外部函数里的变量,内部函数就会在使用该变量的时候寻找该变量,因为该变量在外部函数的AO对象中,内部函数就会将此AO对象地址存入自己的作用域链中,已便访问。所以此AO 对象会被占用,GC不会回收该作用域所占的内存,但是会将外部函数的作用域链中存储的指向该作用域也就是AO对象的地址删除,恢复外部函数初始化时的作用域链(只有一个GO 全局作用域对象,没有AO)。
-
成为闭包函数的方法
-
将内部函数或者使用到了外部函数中的变量的变量return返回到嵌套函数体外
function fun() { var a = 0; return function () { ++a; console.log(a); } } var x1 = fun(); x1(); x1(); var x2 = fun(); x2();
-
使用立即执行函数
function fun(){ for(var i = 0 ; i<10;i++){ ( function (j) { var arr[i]=function (j){ return j; // } })(i) //立即执行,将每次外部变量i作为参数使用到内部函数中 } }
-
-
来吃瓜,具体分析一下
function fun() { var a = 0; return function () { ++a; console.log(a); } } var x1 = fun(); x1(); x1(); var x2 = fun(); x2();
- 在fun函数中定义一个内部函数,并将内部函数返回到函数体外。内部函数中使用外部函数中的变量a,对a进行自增输出。
- 运行代码。生成GO全局对象,存有全局变量x1,x2的声明,变量值为undefined,还有fun()函数,函数值只有一个函数体框架,没有具体的内容。
- 函数一旦被声明就会创建一个scope作用域属性,将GO对象添加到fun函数的作用域链中,索引值为0;
- 程序运行到
var x1 = fun();
返回执行fun()函数,此时会生成一个fun()函数的AO活跃对象索引值改为0GO对象索引值改为1,将a和匿名函数放入到ao中,a赋值为undefined,匿名函数只有一个函数体框架,没有具体内容。 - 匿名函数一被声明就会产生一个scope作用域属性,作用域链中只会存有go全局对象和fun()函数的ao对象索引值为1和0。
- fun()函数代码开始执行,分别将a赋值为0,ao中的a值改为0,执行到
return function () { ++a;console.log(a); }
将该匿名函数返回,该匿名函数指向fun()函数中的作用域链也被返回。go中的x1接受了返回值,值为匿名函数的函数体,但没有内容。本来函数被执行完之后应该回收作用域也就是ao对象占用的空间,但是因为此时的AO对象被匿名函数占用,所以不会将ao对象回收,只会将fun函数的作用域链初始化,即最开始的时只有一个go全局对象,等待下次执行时再次生成一个AO对象,与上一个AO对象并不会产生联系,所以运行x2得到的值和第一次运行x1时相同都是从初始值开始进行自增。 - 运行x1,相当于执行了匿名函数,此时匿名函数生成AO 对象索引值为0。然后对a变量进行自增,但是匿名函数中并没有a变量,然后根据作用域链的索引值顺序进行查找,先是查找指向上级函数的AO对象是否有变量a发现能找到,然后对a的值进行更改,a = 1;执行结束之后再次运行x1,因为两次x1都是由
var x1 = fun();
赋值得到,使用的是相同的作用域链(包括fun函数 的ao对象)所以再次运行时还是会找到此时已经是1的a自行自增。输出为2。