js 闭包

闭包是指有权访问另一个 函数作用域中的变量的函数,创建闭包的常见方式,就是在一个函数内部创建另一个函数。

闭包的特点:

  1.函数嵌套函数,并以函数作为返回值。

  2.内部函数可以访问外部函数的变量

  3.参数和变量不会被回收。

例如:

    function test(){
        var x = 10;
        return function(y){
            return y + x;
        }   
    }
    var t1 = test();
    console.log(t1(9));

在该例子中,在匿名函数从 test()中被返回后,它的作用域链被初始化为包含 test()函数的活动对象和全局变量对象。这样,匿名函数就可以访问在 test()中定义的所有变量。更为重要的是,test() 函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换 句话说,当 test() 函数返回后,其执行环境的作用域链会被销毁,但它的活 动对象仍然会留在内存中;直到匿名函数被销毁后,test()的活动对象才会 被销毁。如下:

    function test(){
        var x = 10;
        return function(y){
            return y + x;
        }
        
    }
    //创建函数
    var t1 = test();
    //调用函数
    var num = t1(9);
    //解除对匿名函数的引用(以便释放内存)
    t1 = null;

随着匿名函数的作用域链被销毁,其他作用域 (除了全局作用域)也都可以安全地销毁了。

闭包与变量

  作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最 后一个值。闭包所保存的是整个变量对象,而不是某个特殊的变量。下面这个例子可以清晰地说 明这个问题。

    function test(){
        var arr = [];
        for(var i = 0; i < 10; i++){
            arr[i] = function(){
                return i;
            }
        }
        return arr;
    }
    var arr = test();
    console.log(arr[1]());//10

这个函数会返回一个函数数组,每个函数返回的都是 最大值 10。因为 当test() 执行完成时,i的值是10,闭包只能取得包含函数中任何变量的最 后一个值,所以每次返回都是10。

可以通过创建另一个匿名函数强制让闭包的行为 符合预期,如下所示。

    function test(){
        var arr = [];
        for(var i = 0; i < 10; i++){
            arr[i] = function(num){
                return function(){
                    return num;
                }
            }(i)
        }
        return arr;
    }
    var arr = test();
    console.log(arr[1]());//1

让匿名函数自执行,参数传进去, 然后返回另一个匿名函数形成闭包,而这个匿名函数就会取得自执行函数的参数,所以就会返回不同的值

关于this对象

  虽然闭包 可以访问 外部函数的 所有变量,但是对于this 和  arguments 却存在问题,如下  

    var name = 'window';
    var obj = {
        name: 'obj',
        test: function(){
            return function(){
                return this.name;
            }
        }
    }
    console.log(obj.test()()); // window

这里访问的this 确实全局window 对象,而不是外部函数的this ,每个函数在被调用时都会自动取得两个特殊变量:this 和 arguments。内部函 数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。

也可以让它访问到外部函数的this 如下:

    var name = 'window';
    var obj = {
        name: 'obj',
        test: function(){
            var that = this;
            return function(){
                return that.name;
            }
        }
    }
    console.log(obj.test()()); // obj

这样就可以访问到 外部函数的this了,如果想访问作用域中的 arguments 对 象,必须将对该对象的引用保存到另一个闭包能够访问的变量中。

内存泄漏 

  由于 IE9 之前的版本对 JScript 对象和 COM 对象使用不同的垃圾收集例程。

  因此闭包在 IE 的这些版本中会导致一些特殊的问题。具体来说,如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁。如下:

function assignHandler(){
        var element = document.getElementById("someElement");
        element.onclick = function(){
            alert(element.id);
        };
}

  以上代码创建了一个作为 element 元素事件处理程序的闭包,而这个闭包则又创建了一个循环引用,由于匿名函数保存了一个对 assignHandler()的活动对象的引用,因此 就会导致无法减少 element 的引用数。只要匿名函数存在,element 的引用数至少也是 1,因此它所 占用的内存就永远不会被回收。

这个问题可以通过稍微改写一下代码来解决,如下所示。

function assignHandler(){
  var element = document.getElementById("someElement"); 
var id = element.id; element.onclick = function(){     alert(id);   }; element = null; }

  在上面的代码中,通过把 element.id 的一个副本保存在一个变量中,并且在闭包中引用该变量消 除了循环引用。但仅仅做到这一步,还是不能解决内存泄漏的问题。必须要记住:闭包会引用包含函数 的整个活动对象,而其中包含着 element。即使闭包不直接引用 element,包含函数的活动对象中也 仍然会保存一个引用。因此,有必要把 element 变量设置为 null。这样就能够解除对 DOM 对象的引 用,顺利地减少其引用数,确保正常回收其占用的内存。

原文地址:https://www.cnblogs.com/bruce-gou/p/9679554.html