JavaScript和Java、C#、C++等语言相比有一个特点,它可以进行函数嵌套,由于函数的嵌套造成会出现内部函数引用外部变量的情况,如果通过外部函数生成一个实例,然后再调用这个实例,那么可以看做内部函数在定义它的作用域的外部被引用,这样就形成了内部函数的封闭环境,称之为闭包。
情况1:变量为内部函数的变量
$(function () {
var fun1 = outFun();
fun1();
fun1();
var fun2 = outFun();
fun2();
fun2();
});
function outFun() {
function inFun() {
var i = 0;
i++;
alert(i);
}
return inFun;
}
输出的结果是:
1
1
1
1
分析程序可以得知当i做完内部函数局部变量的时候,每次通过外部函数调用内部函数,该局部变量都会初始化,因此四次调用最终的值均为1。
情况2:变量为全局变量
var i = 0;
$(function () {
var fun1 = outFun();
fun1();
fun1();
var fun2 = outFun();
fun2();
fun2();
});
function outFun() {
function inFun() {
i++;
alert(i);
}
return inFun;
}
输出的结果是:
1
2
3
4
情况3:如果变量在外部函数内定义,但是在内部函数内使用。并且在外部针对外部函数进行两次引用,分别赋值给不同的变量,再对变量进行调用,会发生什么呢?
$(function () {
var fun1 = outFun();
fun1();
fun1();
var fun2 = outFun();
fun2();
fun2();
});
function outFun() {
var i = 0;
function inFun() {
i++;
alert(i);
}
return inFun;
}
输出的结果是:
1
2
1
2
通过不同的引用来调用inFun()函数,在第一次引用生成的fun1实例内部形成一个封闭的环境,变量i会随着每次调用fun1而继承上次的值进行变化,两个引用fun1和fun2之间互不影响,说明两个不同的引用中的i是不同的变量,在内存总有独立的存储位置。现在我们将上面的代码再修改下,看看会发生什么?
$(function () {
var fun1 = outFun();
fun1();
var fun2 = outFun();
fun2();
fun1();
fun2();
});
function outFun() {
var i = 0;
function inFun() {
i++;
alert(i);
}
return inFun;
}
输出结果是:
1
1
2
2
通过结果分析知道,两次引用的两个函数还是互不影响,各自保证自身内部环境独立。
总结
内部函数在定义它的作用域的外部被引用时,就创建了该内部函数的一个闭包。如果内部函数引用了位于外部函数中的变量,当外部函数的引用被多次调用的时候,已经初始化的外部函数变量会一直驻扎在内存中不被释放,因为内部函数这样的闭包环境会一直使用它。