1.理解闭包小段代码
代码1:
function a(){
var n = 0;
this.inc = function(){
n++;
document.writeln(n+'<br>');
};
}
var c = new a();
var c1 = new a();
c.inc();
c1.inc();
代码2:
function counter(){
var n = 0;
function inc(){
n++;
document.writeln(n);
}
return inc;
}
var c = counter();
c();
代码3:
function a(){
var n = 0;
function inc(){
n++;
document.writeln(n);
}
inc();
inc();
}
a();
理解: 有权访问另一个函数作用域内变量的函数都是闭包。
2.关于闭包一个小陷阱
function createFunctions(){
var result = new Array();
for(var i=0;i<10;i++){
result[i] = function(){
return i;
};
}
return result;
}
var funcs = createFunctions();
for(var i=0;i<funcs.length;i++){
document.writeln(funcs[i]());
}
解释:输出了十个10。
原因(真正的执行过程):
var result = new Array(), i;
result[0] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换
result[1] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换
...
result[9] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换
i = 10;
funcs = result;
result = null;
document.writeln(i);//function[0]()就是执行return i语句,就是返回10
document.writeln(i);//function[0]()就是执行return i语句,就是返回10
...
document.writeln(i);//function[0]()就是执行return i语句,就是返回10
PS:随着result被赋值null可得result被垃圾回收机制回收;但不回收i, 因为 i 还在被 function 引用着。
3.闭包的用途
1) 可以读取函数内部的变量。
2)让这些变量的值始终保持在内存中。
代码:
function f1(){
var n = 999;
nAdd = function(){
n+=1;
};
function f2(){
document.writeln(n+"<br>");
}
return f2;
}
var result = f1();
result();
nAdd();
result();
解释:
这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
4.使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
5.两段对比的代码(有助于理解闭包)。
代码1:
var name = 'the window';
var object = {
name : 'My Object',
getNameFunc : function(){
document.writeln(this+"<br>");
return function(){
document.writeln(this+"<br>");
return this.name;
};
}
};
document.writeln(object.getNameFunc()());
输出结果: [object Object]
[object Window]
the window
代码2:
var name = "the window";
var object = {
name:"My Object",
getNameFunc : function(){
var that = this;//对this进行了声明,维护了this的作用域在object内
return function(){
return that.name;
};
}
};
document.writeln(object.getNameFunc()());//输出My Object
6.一个有助于理解闭包的例子:
例子:
function A(){
var x = 1;
return function(){
x++;
document.writeln(x);
}
}
var m1 = A();
m1();//输出2
m1();//输出3
var m2 = A();
m2();//输出2
m1();//输出4
7.一个对比的例子
代码1:
function outerFun()
{
var a =0;
document.writeln(a+"<br>");
}
var a=4;
outerFun();
document.writeln(a+"<br>");
解释: 结果是 0,4 . 因为在函数内部使用了var关键字 维护a的作用域在outFun()内部.
代码2:
function outerFun()
{
//没有var
a =0;
document.writeln(a+"<br>");
}
var a=4;
outerFun();
document.writeln(a+"<br>");
输出:0 0
解释:结果是0,0. 因为 作用域链是描述一种路径的术语,沿着该路径可以确定变量的值 .当执行a=0时,因为没有使用var关键字,因此赋值操作会沿着作用域链到var a=4; 并改变其值.