一、变量的作用域
要理解闭包,首先必须理解JavaScript特殊的变量作用域。
变量的作用域就两种:全局变量和局部变量。
JavaScript语言的特殊在于函数内可以直接读取全局变量。\
var n = 99;
function f1(){
alert(n);
}
f1();//99
复制代码
另一方面,函数外部不能读取函数内部的局部变量
function f1(){
n = 99;
}
f1();
alert(n);//99
复制代码
二、如何从外部读取局部变量
各种原因之下,我们有时候需要得到函数内部的局部变量,但是正常是实现不了的,只有通过别的方法才能实现。 那就是在函数内部,再定义一个函数。
function f1(){
var n = 99;
function f2(){
alert(n);//99
}
}
复制代码
上面所示的代码中,函数f2包含着f1内部,此时f1所有的局部变量对于f2来说都是可见的。但是如果反过来,f2的局部变量对f1就是不可见的。这就是JavaScript语言特有的“链式作用域”结构(chain scope),子对象会逐级向上寻找父对象的变量,所以,父对象的所有变量,对子对象都是可见的,反之不成立。
既然f2可以读取f1中的局部变量,那只要把f2作为返回值的话,我们就可以读取到f1当中的局部变量
function f1(){
var n = 99;
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result();//99
复制代码
三、闭包的概念
上一节代码中的f2函数,就是闭包。
各种书上的“闭包”(closure)定义抽象且难懂,我理解的是,闭包就是能够读取其他函数内部变量的函数。
在JavaScript中,只有函数内部子函数才能读取局部变量,所以可以把闭包看成一个“定义在函数内部的函数”
所以本质上,闭包就是把函数内部和函数外部连接起来的一个桥梁 通过以下的代码来理解以下上述那句话
function f1(){
var n = 99;
nAdd = function(){n += 1}
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result();//99
nAdd();
result();//100
复制代码
这段代码里,result其实就是闭包f2函数,它一共运行两次,第一次的值是99,第二次的值是100,这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用过以后被清除。
为什么会这样?
原因在于f1是f2的父函数,而f2被赋给了一个全局变量。导致f2一直在内存中,f2的存在依赖于f1,所以f1也一直存在于内存中,不会在调用后被回收。
另外,代码中还有一个值得注意的地方,就是“nAdd = function(){n += 1}”,首先在nAdd的前面没有使用var关键字,所以nAdd定义的是一个全局变量,不是局部变量。其次,nAdd是一个匿名函数,而它本身也是一个闭包,因此nAdd相当于一个setter,可以在函数外部对函数内部的局部变量进行操作。
五、使用闭包的注意点
1)由于闭包会使得函数中的变量全部被保存在内存中,内存消耗大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏。解决方法是,在推出函数之前把不使用的局部变量全部删除。
2)闭包可以在父函数,改变父函数内部变量的值。所以,如果把父函数当做对象(object)使用,把闭包当做父函数的公用方法,把内部变量当做私有属性,这个时候要小心别随意改变父函数内部变量的值。
给自己的思考题
var name = "The Window";
var object = {
name : "My Object",
getNameFunc:function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
复制代码
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
复制代码