@linux1s1s
2015-04-26T16:21:39.000000Z
字数 3435
阅读 1962
JavaScript
我们先从一个基本的内部类开始
function outerFn() {
document.write("Outer function<br/>");
function innerFn() {
document.write("Inner function<br/>");
}
}
假如我们想在outerFn()外调用innerFn()肿么办
function outerFn() {
document.write("Outer function<br/>");
function innerFn() {
document.write("Inner function<br/>");
}
}
innerFn();
上面的代码会出现JavaScript错误,那么该怎么调用呢?
JavaScript允许开发人员像传递任何类型的数据一样传递函数,也就是说,JavaScript中的内部函数能够逃脱定义他们的外部函数。
var globalVar;
function outerFn() {
document.write("Outer function<br/>");
function innerFn() {
document.write("Inner function<br/>");
}
globalVar = innerFn;
}
outerFn();
globalVar();
这种逃逸方式是:内部函数指定给一个全局变量
function outerFn() {
document.write("Outer function<br/>");
function innerFn() {
document.write("Inner function<br/>");
}
return innerFn;
}
var fnRef = outerFn();
fnRef();
这种逃逸方式是:通过在父函数的返回值来获得内部函数引用。
这种即使离开函数作用域的情况下仍然能够通过引用调用内部函数,意味着只要存在调用内部函数的可能,JavaScript就需要保留被引用的函数。而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间.
闭包是指有权限访问另一个函数作用域的变量的函数
闭包的特性:
function outerFn() {
document.write("Outer function<br/>");
function innerFn() {
var innerVar = 0;
innerVar++;
document.write("Inner function\t");
document.write("innerVar = "+innerVar+"<br/>");
}
return innerFn;
}
var fnRef = outerFn();
fnRef();
fnRef();
var fnRef2 = outerFn();
fnRef2();
fnRef2();
运行结果:
Outer function
Inner function innerVar = 1
Inner function innerVar = 1
Outer function
Inner function innerVar = 1
Inner function innerVar = 1
var globalVar = 0;
function outerFn() {
document.write("Outer function<br/>");
function innerFn() {
globalVar++;
document.write("Inner function\t");
document.write("globalVar = " + globalVar + "<br/>");
}
return innerFn;
}
var fnRef = outerFn();
fnRef();
fnRef();
var fnRef2 = outerFn();
fnRef2();
fnRef2();
运行结果:
Outer function
Inner function globalVar = 1
Inner function globalVar = 2
Outer function
Inner function globalVar = 3
Inner function globalVar = 4
父函数的局部变量被内部类引用(有兴趣可以了解一下作用域链和活动对象的知识)
function outerFn() {
var outerVar = 0;
document.write("Outer function<br/>");
function innerFn() {
outerVar++;
document.write("Inner function\t");
document.write("outerVar = " + outerVar + "<br/>");
}
return innerFn;
}
var fnRef = outerFn();
fnRef();
fnRef();
var fnRef2 = outerFn();
fnRef2();
fnRef2();
这一次结果非常有意思,也许或出乎我们的意料
Outer function
Inner function outerVar = 1
Inner function outerVar = 2
Outer function
Inner function outerVar = 1
Inner function outerVar = 2
我们看到的是前面两种情况合成的效果,通过每个引用调用innerFn都会独立的递增outerVar。也就是说第二次调用outerFn没有继续沿用outerVar的值,而是在第二次函数调用的作用域创建并绑定了一个一个新的outerVar实例,两个计数器完全无关。
function outerFn() {
var outerVar = 0;
document.write("Outer function<br/>");
function innerFn1() {
outerVar++;
document.write("Inner function 1\t");
document.write("outerVar = " + outerVar + "<br/>");
}
function innerFn2() {
outerVar += 2;
document.write("Inner function 2\t");
document.write("outerVar = " + outerVar + "<br/>");
}
return { "fn1": innerFn1, "fn2": innerFn2 };
}
var fnRef = outerFn();
fnRef.fn1();
fnRef.fn2();
fnRef.fn1();
var fnRef2 = outerFn();
fnRef2.fn1();
fnRef2.fn2();
fnRef2.fn1();
运行结果:
Outer function
Inner function 1 outerVar = 1
Inner function 2 outerVar = 3
Inner function 1 outerVar = 4
Outer function
Inner function 1 outerVar = 1
Inner function 2 outerVar = 3
Inner function 1 outerVar = 4
innerFn1和innerFn2引用了同一个局部变量,因此他们共享一个封闭环境。当innerFn1为outerVar递增一时,就为innerFn2设置了outerVar的新的起点值,反之亦然。我们也看到对outerFn的后续调用还会创建这些闭包的新实例,同时也会创建新的封闭环境,本质上是创建了一个新对象,自由变量就是这个对象的实例变量,而闭包就是这个对象的实例方法,而且这些变量也是私有的,因为不能在封装它们的作用域外部直接引用这些变量,从而确保了了面向对象数据的专有性。
看完上面的基本知识,你或许对闭包有了一个比较浅显的认识,学过C/C++的同学应该理解起来比较容易,因为闭包特别像是指针函数,但是有不是完全是,因为还有作用域的概念。如果想更深入的认识闭包可以进一步学习JavaScript作用域链和活动对象。
本文转自:http://www.cnblogs.com/dolphinX/archive/2012/09/29/2708763.html 部分内容做了修改,特此说明。