• 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 对象的引 用,顺利地减少其引用数,确保正常回收其占用的内存。

  • 相关阅读:
    应用六:Vue之父子组件间的三种通信方式
    应用五:Vue之ElementUI 表格Table与分页Pagination组件化
    应用四:Vue之VUEX状态管理
    Vue 中使用 sass 或 scss 语法配置
    Sass 中文注释导致编译错误
    Sass 的安装及命令行使用
    video 标签
    原生JS添加删除Class
    HTML5 面试选题
    CSS 常用属性初始化标签名
  • 原文地址:https://www.cnblogs.com/bruce-gou/p/9679554.html
Copyright © 2020-2023  润新知