林炳文Evankaka原创作品。转载请注明出处http://blog.youkuaiyun.com/evankaka
摘要:本文主要讲了JavaScript闭包的原理、特点并用一些实例做了证明
一、闭包的概念与好处
1.1、什么是闭包?
闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
闭包的特点:
1. 作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
2. 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
简单的说,Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
闭包:是指有权访问另外一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另外一个函数
1.2、javascript的垃圾回收原理
1.在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;
2.如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。
1.3、使用闭包的好处
那么使用闭包有什么好处呢?使用闭包的好处是:
1.希望一个变量长期驻扎在内存中
2.避免全局变量的污染
3.私有成员的存在
二、实例讲解
内部函数
让我们从一些基础的知识谈起,首先了解一下内部函数。内部函数就是定义在另一个函数中的函数。例如:
function outerFn () {
functioninnerFn () {}
}
innerFn就是一个被包在outerFn作用域中的内部函数。这意味着,在outerFn内部调用innerFn是有效的,而在outerFn外部调用innerFn则是无效的。
当function里嵌套function时,内部的function可以访问外部function里的变量。
如下:
<!DOCTYPE html>
<meta charset="utf-8" />
<html>
<head>
</head>
<body>
<button οnclick="fun()">请点击这里</button>
<script>
function fun()
{
outfun(1);
}
function outfun(x) {
var tmp = 1;
function infun(y) {
alert(x + y + (++tmp));
}
infun(10);
}
</script>
</body>
</html>
输出结果;
不管执行多少次,都会alert 13,因为infun能访问outfun的参数x,也能访问outfun的变量tmp。但,这还不是闭包。当你return的是内部function时,就是一个闭包。内部function会close-over外部function的变量直到内部function结束。
将函数更改成如下就是闭包了:
<!DOCTYPE html>
<meta charset="utf-8" />
<html>
<head>
</head>
<body>
<button οnclick="fun()">请点击这里</button>
<script>
var test = outfun(1);
function fun()
{
test(10);
}
function outfun(x) {
var tmp = 1;
return function infun(y) {
alert(x + y + (++tmp));
} //这样才是闭包
}
</script>
</body>
</html>
这里每次点击后结果都会加1
第一次点:
第二次点:
当然, 我们也可以使用全局变量来调用闭包
再来看一个例子
function f1(){
var n=999;
nAdd=function(){n+=1}//所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,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,可以在函数外部对函数内部的局部变量进行操作。
JavaScript中所有的function都是一个闭包。不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”。看下面这段代码:
function a() {
var i = 0;
function b() { alert(++i); }
return b;
}
var c = a();
c();
这样在执行完var c=a()后,变量c实际上是指向了函数b,再执行c()后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,为什么?因为函数a外的变量c引用了函数a内的函数b,就是说:当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。
看完了上面,闭包其实在json中也可以来使用,这种方法在很多框架中还经常见到,最基础如下:
<script>
var aaa = (function(){
var a = 1;
function bbb(){
a++;
alert(a);
}
function ccc(){
a++;
alert(a);
}
return {
b:bbb, //json结构
c:ccc
}
})();
aaa.b(); //2
aaa.c() //3
</script>
这里使用了json,返回是一个json
我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,
那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。
var CachedSearchBox = (function(){
var cache = {},
count = [];
return {
attachSearchBox : function(dsid){
if(dsid in cache){//如果结果在缓存中
return cache[dsid];//直接返回缓存中的对象
}
var fsb = new uikit.webctrl.SearchBox(dsid);//新建
cache[dsid] = fsb;//更新缓存
if(count.length > 100){//保正缓存的大小<=100
delete cache[count.shift()];
}
return fsb;
},
clearSearchBox : function(dsid){
if(dsid in cache){
cache[dsid].clearSelection();
}
}
};
})();
CachedSearchBox.attachSearchBox("input");
这样我们在第二次调用的时候,就会从缓存中读取到该对象,如果上面的例子看不懂,那就看下面的吧
var db = (function() {
// 创建一个隐藏的object, 这个object持有一些数据
// 从外部是不能访问这个object的
var data = {};
// 创建一个函数, 这个函数提供一些访问data的数据的方法
return function(key, val) {
if (val === undefined) { return data[key] } // get
else { return data[key] = val } // set
}
// 我们可以调用这个匿名方法
// 返回这个内部函数,它是一个闭包
})();
db('x'); // 返回 undefined
db('x', 1); // 设置data['x']为1
db('x'); // 返回 1
// 我们不可能访问data这个object本身
// 但是我们可以设置它的成员
三、使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便
改变父函数内部变量的值。