闭包:当内部函数被保存到外部时,将生成闭包。闭包会导致原有作用域链不会释放,造成内存泄漏。
内存泄漏:产生的闭包会占用内存,闭包越多,可用内存越少,从可用内存角度看,内存就像泄漏一样越来越少。
先看一段代码:
function add() {
var num=0;
function a(){
console.log(++num);
}
return a;
}
var myAdd = add();
myAdd();//输出1
myAdd();//输出2
函数a()被保存到函数add()外的全局变量,这里就产生了闭包。
具体讲解得从作用域链讲起。
- 在执行add()方法前一刻,生成add()的AO
AO{
num:undefind,
myAdd:undefind
}
此时add()作用域链为 0--AO
1--GO
- 执行add()后AO发生改变
AO{
num:0,
myAdd:function a() {}
}
此时add()作用域链为 0--AO
1--GO
- 执行a()前一刻,生成a()的AO
AO{
}
此时a()作用域链为 0--a()自己的AO
1--add()的AO
2--GO
- 执行a()后a()会切断与自己创建出的AO的关联,但还会保留父级函数的AO
此时a()作用域链为0--add()的AO
1--GO
- 程序执行
return a后add()方法执行完成,切断与自己创建出的AO的关联,但a()并没有切断与其的关联,此时就产生了闭包,add()的AO没有被释放。
此时add()作用域链为0--GO
此时a()作用域链为0--add()的AO
1--GO
- 程序执行
var myAdd = add();此时a()被全局变量myAdd接收 - 程序执行
myAdd()就等于执行a(),在执行a()创建出自己的AO
此时a()作用域链为 0--a()自己的AO
1--add()的AO
2--GO
执行a()
function a(){
console.log(++num);
}
先从自己的AO中找num属性,没有,再去父级的add()中的AO找,此时找到num,值为0。
所以控制台输出1,并把num值更新为1
8.程序再次执行 myAdd()
function a(){
console.log(++num);
}
还是先从自己的AO中找num属性,没有,再去父级的add()中的AO找,此时找到num,值为1。
所以控制台输出2,并把num值更新为2
下面再来一道面试中常见题
function test() {
var arr=[];
for(var i=0;i<10;i++){
arr[i]=function () {
console.log(i);
}
}
return arr;
}
var myArr=test();
for (var j=0;j<10;j++){
myArr[j]();
}
此时控制台输出的是10个10,并不是我们想象中的0~9,接下来我们就用上面的方法来解释这道题。
- 在执行test()方法前一刻,生成tset()的AO
AO{
arr:undefind,
i:undefind
}
此时test()作用域链为 0--AO
1--GO
- 执行test()后AO发生改变
AO{
arr:[arr[i]:function () {
console.log(i);//注意,此时并不会执行console.log(i),而是直接将这句赋值给对应的arr[i],因为这里并没有执行arr[i](),只是单纯的赋值表达式。
}],
i:i//此i为0~10,是真实的值,当i为9时,arr[9]=function() {console.log(i)},i自增为10,此时再进for循环,不满足条件,退出for循环
}
在for循环结束后,arr的值应为
arr[0]=function(){console.log(i)};
arr[1]=function(){console.log(i)};
arr[2]=function(){console.log(i)};
....
arr[8]=function(){console.log(i)};
arr[9]=function(){console.log(i)};
- 执行
return arr; - 执行
var myArr=test();此时myArr就是数组arr[] - 程序执行for循环中
myArr[j]();,此时myArr[]中的值就是第二步中arr[]的值。执行myArrj就是执行function(){console.log(i)},此时i从AO中查找,在test方法执行完时,i已经变为10,所以控制台会输出10个10。