js 函数闭包

闭包的基本概念

在js中,闭包指的是有权访问另一个函数作用域中的变量的函数,最常见的就是在函数中创建函数,即如下:

js 函数闭包
function outfunction(outnum){
    return function(){
        var innum = 1;
        console.log(outnum);
        console.log(innum);
    }
}
js 函数闭包

在代码中,用红色标出的就是内部函数引用外部函数值的地方,实现原理是在闭包函数执行时,遇到变量首先会检索当前执行环境下的变量,即活动对象,然后在未找到的情况下,向外部函数查找,这样直至找到全局对象为止。这也是闭包的作用域链。

闭包的简单应用

闭包的作用域链的特性使得闭包函数取得的任何一个变量的最后更新后的值,因为闭包保存的是整个变量对象,而不是一个个特殊的变量,例子如下:

js 函数闭包
function difFunc(){
    var arr = new Array();
    for(var i = 0;i<10;i++){
        arr[i] = function(){
            console.log(i);
        }
    }
    return arr;
}
js 函数闭包

此例是为了在数组中加一系列函数,每隔函数返回的值递增,看起来似乎可行,但是运行后发现,返回的都是10。这是因为对i的引用并不是以保存特殊的一个变量的方式进行的,而是保存了外部的整个活动对象,然后再搜索得到i的,当arr生成完毕之后,i已经等于10了,所以每个函数打印出来的都是10 。 但是我们可以创建一个闭包让其保存一个i的副本。

js 函数闭包
function difFunc(){
    var arr = new Array();
    for(var i = 0;i<10;i++){
        arr[i] = (function(num){
            return function(){
                console.log(num);
            }
        })(i);
    }
    return arr;
}
js 函数闭包

这样就实现了随着i增加返回不同的数的效果。

内存泄露

由于IE9之前对变量的垃圾回收机制与寻常的(标记清除)不同,即采用的是引用计数法,所以在闭包中一些特定的环境下,就无法清除内存,比如以下这种情况:

function varClear(){
    var div = document.getElementsByTagName(‘div‘)[0];
    div.onclick = function(){
        alert(div.id);
    }
}

这个函数中为某个div创建了一个点击事件的闭包函数,而在函数中又有了一个循环引用,因为匿名函数保存了一个对于varClear的活动对象的引用,于是无法减少对div的引用数,以至于只要闭包函数存在,那么div的引用数至少是1,那么回收机制就无法回收div了。不过,通过稍微修改代码,就可以避免这个问题。

js 函数闭包
function varClear(){
    var div = document.getElementsByTagName(‘div‘)[0],
    id = div.id;
    div.onclick = function(){
        alert(id);
    }
    div = null;
}
js 函数闭包

即再创建一个id的变量作为div.id的副本,在闭包中调用id,这样消除了循环引用问题,但是这还不够,因为闭包是对外部的整个活动对象进行保存,不仅仅是一个id,所以在最后又对div设置为null,这样来减少引用数,确保内存正常回收。

js 函数闭包

上一篇:JQuery Introduction


下一篇:学习Javascript有感