首页 > js学习 > js基础知识 > javascript中的闭包①
2016
12-16

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;//解除引用

}

如果并没有使用解除引用,那么需要等到浏览器关闭才得以释放。

最后编辑:
作者:qingheluo
这个作者貌似有点懒,什么都没有留下。