简单的来说,什么时候形成闭包呢?大多时候当一个内部函数被保存到外部时会形成闭包.
看一段简单的代码
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 1 2 3 4 5 6 7 8 9
,然而并不是,这里就涉及了闭包。
正确的打印结果是10 10 10 10 10 10 10 10 10 10
大多数人会认为当内部for循环执行时就会立即将此时的i值赋给打印值,其实并不是。
test函数执行时,每次for循环都创建了一个函数保存在arr里面,但是函数内部是什么并不知道,此时
{
document.write(i + "");
}
这一段代码没有立即实行,他只是告诉arr[i]里保存了一个函数,相当于创建一个arr[i] = 函数
这样一个东西。当·执行到return语句时。arr这个数组被保存到外部,也就是保存了十个相同的函数,每一个都有自己的执行期上下文和作用域链,但是此时的i已经变为了10,所以虽然函数执行十次,但每次打印出来的i都是同一个.他们的作用域里面保存着test的作用域,当然i值也就是用着同一个,典型的闭包。
那么要打印出不同值怎么办,来看下面的函数
function test(){
var arr = [];
for(var i = 0;i<10;i++){
(function(j){
arr[j] = function(){
document.write(j + "");
}
}(i));
}
return arr;
}
var myarr = test();
for(var j = 0;j < 10;j++){
myarr[j]();
}
这一次我们给他加了一层立即执行函数,当灭磁读取到function(j)时都会立即执行一次。i作为函数的实参,j为函数的形参,此时的函数直接相互独立,形成十个不同的函数·并不会受i值变化的影响,因此会打印出来0 1 2 3 4 5 6 7 8 9
下面再来看一个函数
function add(){
var num = 0;
function a(){
console.log(++num);
}
return a;
}
var myadd = add();
myadd();
myadd();
myadd();
这一次函数打印出来的是1 2 3
。这是一个很典型的闭包。
在add函数被执行时,他形成了一个自己的AO,a函数定义是他拿的是 add的AO以及GO,并且a函数被保存到了外部。
当add函数执行完毕后他的执行期上下文被销毁,这里就容易不懂,那么既然a的2执行期上下文被销毁,那么num应该不存在了,怎么会打印出num值。
其实在a函数被保存到外部时,就已经形成了一个闭包,他的作用域链里保存着add的执行期上下文,虽然add被销毁,但是a却被保存了,导致add的执行期上下文保存在a里面而无法被释放。此时的num值已经成为了a函数所有的,不存在于add里面,当然每次执行时num不会被初始化为0,所以num值会随着a函数的的执行而改变。
总的来说,闭包就是当内部函数被保存在外部时,导致原有的内部函数上级作用域链无法被释放,跟随着内部函数被保存到外部,此时的上级作用域链里的所有东西都归内部函数所有,相当于在内部函数里面定义的变量。当多个内部函数同时形成闭包时则用的是同一个作用域,里面的东西为函数公有,当你在一个函数里面改变某一变量的值,那么在另一函数里面调用这个变量时变量值已经是改变后的数值,
- 其实很好理解,顾名思义,内存泄露则内存少了,闭包形成时原有作用域链没有被及时销毁,继续占用内存。就像手里握着一把沙子,沙子慢慢流出,也就是泄露,那么你手中的沙子会越来越少,内存泄漏就是这个道理。