JS 闭包

2019-07-30  本文已影响0人  timehorse

闭包

mdn上定义是:函数和声明该函数的词法环境的组合。

维基定义是:闭包 词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外

支持闭包的语言中,通常函数会是一等公民。也就是函数可以像普通变量一样使用。

相信你没有接触过的话读完只会更困惑。

我们知道JS是支持闭包的,下面看一个JS的🌰。

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}

var foo = checkscope();
foo();

该例子中:函数checkscope内仍有一个函数f,而且f内引用了一个外部变量scope,其定义在checkscope中,但使用在f中。在调用时,分了两步:第一步执行checkscope(),其执行结束后有什么结果呢?答案是scope变量并未销毁,因为f函数仍然引用了它。故第二步执行foo()才没有问题。

这就是闭包的形式。

语言对比

像上面的这种函数套函数的写法,就是闭包的形式之一。有许多语言支持闭包,比如python,lua。并不是所有语言呢都支持这样,比如c语言,我们看一下c语言的函数,假如如下:

void func1(){
    printf("http://c.biancheng.net");
    void func2(){
        printf("C语言小白变怪兽");
    }
}

上述定义是❌的,因为C语言的函数不能嵌套定义。

闭包理解

闭包允许内层函数引用父函数中的变量。注意这里说的是对外层的引用。

示例:

/**
 * <body>
 * <ul>
 *     <li>one</li>
 *     <li>two</li>
 *     <li>three</li>
 *     <li>one</li>
 * </ul>
 */

var lists = document.getElementsByTagName('li');
for(var i = 0 , len = lists.length ; i < len ; i++){
    lists[ i ].onmouseover = function(){
        alert(i);    
    };
}

这里鼠标移过每个li元素,总是弹出4。

为什么呢?

首先在匿名函数( function(){ alert(i); })内部查找是否定义了 i,结果是没有定义;因此它会向上查找,查找结果是已经定义了,并且i的值是4(循环后的i值);所以,最终每次弹出的都是4。

其中一种解决办法是:

var lists = document.getElementsByTagName('li');
for(var i = 0 , len = lists.length ; i < len ; i++){
    (function(index){
        lists[ index ].onmouseover = function(){
            alert(index);    
        };                    
    })(i);
}

内存泄漏

闭包十分容易造成浏览器的内存泄露。JS采用的是一种自动垃圾回收机制。当一个对象无用的时候,即程序中无变量引用这个对象时,就会从内存中释放掉这个变量。

循环引用

A->B->C->B :这里增加了C的某一属性引用B对象,如果这是清除A,那么B、C不会被释放,因为B和C之间产生了循环引用。

闭包非常容易创建循环引用,故有可能导致内存泄漏。

上一篇下一篇

猜你喜欢

热点阅读