javascript中的闭包①
闭包:闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见的方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。
通过闭包可以返回局部变量
function box(){var user = 'Lee'; return function(){return user;};}//通过匿名函数返回box()局部变量user
alert(box()()); //通过box()()来直接调用匿名函数返回值
var b = box();alert(b());//另一种调用匿名函数返回值
使用闭包有一个优点,也是它的缺点:就是可以把局部变量驻留在内存中,可以避免使用全局变量。(全局变量污染导致应用程序不可预测性,每个模块都可调用必将引来灾难,所以推荐使用私有的,封装的局部变量)。
通过局部变量无法实现累加
function box() {var age = 100; age ++; return age;}
alert(box()); //101
alert(box()); //101,第二次调用依然是101,无法实现累加,因为又被初始化了
通过闭包可以实现局部变量的累加
function box() {var age = 100;
return function () {age ++; return age;}
}
var b=box(); //只有在定义变量b的时候才调用函数box()并初始化,之后调用变量b不会初始化box()。
alert(b()); //101,调用匿名函数
alert(b()); //102,第二次调用匿名函数,实现了累加
这里如果使用box()()方法调用匿名函数的话不会实现累加,因为每次调用box()时age还是会初始化。
由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包。
所以在调用完之后加一个b=null;来解除引用,等待资源回收
作用域链的机制导致一个问题,在循环中里的匿名函数取得的任何变量都是最后一个值:
function box(){var arr = [];
for (var i = 0; i < 5; i++) {
arr[i] = function () {return i;};
}
return arr;
}
var b = box(); //得到函数数组
for (var i = 0; i < b.length; i++){alert(b[i]());}//输出每个函数的值,都是最后一个值5
上面的例子输出的结果都是循环后得到的最大的i值。因为b[i]调用的是匿名函数,匿名函数并没有自我执行,等到调用的时候,box()已执行完毕,局部变量i早已变成5,所以最终的结果就是5个5。我们可以采用以下方法解决这个问题:
1、我执行匿名函数
function box() {var arr = [];
for (var i = 0; i < 5; i++) {
arr[i] = (function (num){return num;})(i); //自我传参并且执行
}
return arr;
}
var b = box();
for (var i = 0; i < b.length; i++) {alert(b[i]);}//这里返回的是数组,直接打印即可
上例中我们让匿名函数进行自我执行,导致最终返回给arr[i]的是数组的中的每个的值而不是函数了。最终b[0]-b[4]中保留了0,1,2,3,4的值。
2、匿名函数下再做个匿名函数
function box() {
var arr = [];
for (var i = 0; i < 5; i++) {
arr[i] = (function (num) {
return function () {return num;}//原理和上面例子一样,但是返回的是一个匿名函数
//function (){return num;},该函数外层的匿名函数已经自我传参并执行
})(i);
}
return arr;
}
var b = box();
for (var i = 0; i < b.length; i++) {alert(b[i]());}//这里通过b[i]()函数调用即可
上面两个示例中,我们通过匿名函数自我执行,每次循环的i值,都是调用方通过按值即时传递的,所以最终返回的都是指定的递增的i。而不是box()函数里的i。
闭包中的this对象
在闭包中使用this对象也可能会导致一些问题,this对象是在运行时基于函数的执行环境绑定的,如果this在全局范围就是window,如果在对象内部就指向这个对象。而闭包却在运行时指向window的,因为闭包并不属于这个对象的属性或方法。
var user = 'The Window';
var obj = {
user : 'The Object',
getUserFunction : function () {
return function () {return this.user;};//闭包不属于obj,里面的this指向window
}
};
alert(obj.getUserFunction()()); //The window
可以强制指向某个对象:alert(obj.getUserFunction().call(obj));//The Object
也可以从上一个作用域中得到对象
getUserFunction : function () {
var that = this; //从对象的方法里得对象
return function () {return that.user;};
}
内存泄漏
由于IE的JScript对象和DOM对象使用不同的垃圾收集方式,因此闭包在IE中会导致一些问题。就是内存泄漏的问题,也就是无法销毁驻留在内存中的元素。
function box() {
var oDiv = document.getElementById('oDiv'); //oDiv用完之后一直驻留在内存
oDiv.onclick = function () {alert(oDiv.innerHTML);};//这里用oDiv导致内存泄漏
}
那么在最后应该将oDiv解除引用来避免内存泄漏。
function box() {
var oDiv = document.getElementById('oDiv');
var text = oDiv.innerHTML;
oDiv.onclick = function () {
alert(text);
};
oDiv = null;//解除引用
}
如果并没有使用解除引用,那么需要等到浏览器关闭才得以释放。