之前对闭包总是一知半解,网上的大部分解释也是晦涩难懂,查了一些资料,写一些自己对闭包的理解。
闭包:作用域的艺术
要理解闭包,还是要从javascript变量作用域说起。
js变量分为局部变量和全局变量。
在 function 内部声明的是局部变量,在函数外是无法获取到局部变量的。如
function foo1(){
var a = 2;
}
console.log(a);//a is not defined
除非将局部变量a作为返回值调用,此时函数外部就获取到了局部变量a的值。如:
function foo1(){
var a = 2;
return a;
}
var data = foo1();
console.log(data);//2
举一个简单的例子来说一下闭包。假如你想统计一个页面所有按钮的点击次数。
你可以设置一个全局变量n,每点击一次运行n++,
假设这个计算过程语句再多一些,那么你可以设置一个代码块来执行,如
var n = 0;//全局变量
function add(){
n++;
}
那么现在有个愿望,我不想要别的函数也能改变n的值,想要n的变化只与函数add有关,就是说我要把n隐藏起来,只有add能使用它。
就好像变量n只是函数add的局部变量一样。
那么就把n写成是局部变量吧。
返回n,让计数器能够知道n的值。愿望1达成,n只能由函数add调用。
function add(){
var n = 0;//局部变量
n += 1;
return n;
}
新问题又来了,
点击一次 add();//1
点击两次 add();//1
点击三次 add();//1
...
局部变量的值在函数运行完毕后就销毁了,导致每一个点击计数结果都是1.
这就蛮尴尬了呀,可是我们不能放弃,
走曲线救国的道路
var add = (function () {
var n = 0;
return function () {return n += 1;}
})();
点击一次 add();//1
点击两次 add();//2
点击三次 add();//3
...
OK,大功告成。
下面,解释一下运行机制。为方便描述,将匿名函数修改一下,结果如下:
var add = f1();
function f1() {
var n = 0;
function f2() {return n += 1;}
return f2;
};
首先: add 此时为一个全局变量,值为f2,即
function f2() {return n += 1;}
由于f2是定义在f1内部的函数,因此可以调用f1的局部变量n。
又因为add此时为全局变量,因此add()在运行过后 n 还存在于内存中。
需要注意的是,add()运行后 n 的变化,并不影响原f1 和 f2 中的n值。
如
var add = f1();
function f1() {
var n = 0;
function f2() {return n += 1;}
return f2;
};
add();//1
add();//2
add();//3
var add2 = f1();
add2();//1
add2();//2
add2();//3
总结:
我理解的闭包应该包含三个部分:
1、父函数,其中声明了不想被其他外部代码调用的局部变量;
2、子函数,可以调用父函数声明的局部变量并加以处理;
3、返回值,以子函数做为返回值赋值给全局变量,在该全局变量未被销毁的情况下,变量的运算结果将保存在内存中。