zoukankan      html  css  js  c++  java
  • JS 中闭包的变量 闭包与this

    闭包与变量:

    作用域链的一个副作用,闭包只能取得包含函数中任何变量的最后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。

        function fn1(){
            //创建一个数组
            var arr = new Array();
            //为数组赋值,此时i是保存在fn1 这个作用域中
            for (var i = 0; i <10; i++) {
                arr[i] = function(){
                    return i
                }
            }
        return arr;
       }
    
       var fs = fn1();
       for (var i = 0; i < fs.length; i++) {
           fs[i]();   //此时通过闭包来调用函数,会去上一级作用域中找,这是i的值已经是10,所以会连续输出10个  10;
       };

    解决方法:通过创建另一个匿名函数强制让闭包的行为符合预期,

     function fn1(){       
            var arr = new Array();     
            for (var i = 0; i <10; i++) {
                arr[i] = (function(num){
                    return function(){
                        return num;
                    }
                })(i);  //此时 有大量的作用域
            }
        return arr;
       }
    
       var fs = fn1();
       for (var i = 0; i < fs.length; i++) {
           fs[i]();    //此时就是 0,1,2,3,4,5,6,7,8,9  ,
       };

    消耗大量的内存,

    闭包的this问题:

    在闭包中使用 this 对象也可能会导致一些问题,this 对象是在运行时基于函数的执行环境绑定的:
    在全局函数中, this 等于 window,当函数作为某个对象的方法调用时, this 等于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。

    var name ="A";
       var object = {
            name: "obj",
            say: function(){
                return function(){
                    return this.name;
                }
            }
       }
       object.say()();  //此时,输出的是A,
    当调用object.say()后,object的函数 内存空间已经释放,但是里面的闭包 依然存在,此时这个this就是指向window

    解决方法:

     var name ="A";
       var object = {
            name: "obj",
            say: function(){
                //此时 that 就指向了 object 
                var that = this;
                return function(){
                    return that.name;
                }
            }
       }
     object.say()();  //此时,输出的是obj,

    内存的泄露:

    如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁。

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

    创建了一个作为 element 元素事件处理程序的闭包,匿名函数保存了一个对 assignHandler()的活动对象的引用,只要匿名函数存在, 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。

    块级作用域:

    JavaScript 没有块级作用域的概念,不管是使用循环还是判断之后,这个变量会一直存在,所以当全局中使用了 循环 或者判断后,这个变量可能会影响到函数的变量,所以除非特殊情况,不要使用全局变量,且 全局变量在作用域链最顶部,访问最慢,

    for (var i = 0; i < 10; i++) {
          
      };
      alert(i);   //此时i就是 10;

    解决的方式是匿名函数,用作块级作用域(通常称为私有作用域)的匿名函数的语法如下所示。

     (function(){
        //这里是块级作用域
        })();

    定义并立即调用了一个匿名函数。在一个由很多开发人员共同参与的大型应用程序中,过多的全局变量和函数很容易导致命名冲突。而通过创建私有作用域,每个开发人员既可
    以使用自己的变量,又不必担心搞乱全局作用域。
    这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。

    私有变量:

    私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。

    function add(num1, num2){
        var sum = num1 + num2;
        return sum;
    }这个函数内部,有 3 个私有变量: num1、 num2 和 sum。在函数内部可以访问这几个变量,但在函数外部则不能访问它们。如果在这个函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访
    问这些变量。

    有权访问私有变量和私有函数的公有方法称为特权方法(privileged method)。

    function Person(name){
        //特权方法
        this.setName = function(value){
            name = value;
        }
        //特权方法
        this.getName = function(){
            return name;
        }
     }
     var p = new Person("Linda");   p.setName("Joke");   p.getName(); //Joke

    两个特权方法: getName()和 setName()。这两个方法都可以在构造函数外部使用,而且都有权访问私有变量 name。但在 Person 构造函数外部,没有任何办法访问 name。
    它们作为闭包能够通过作用域链访问 name。私有变量 name在 Person 的每一个实例中都不相同,因为每次调用构造函数都会重新创建这两个方法。
    构造函数模式的缺点是针对每个实例都会创建同样一组新方法,而使用静态私有变量来实现特权方法就可以避免这个问题。

    静态私有变量:

    多查找作用域链中的一个层次,就会在一定程度上影响查找速度。而这正是使用闭包和私有变量的一个显明的不足之处

    模块模式

    模块模式通过为单例添加私有变量和特权方法能够使其得到增强,

    var singleton = (function(){
        //私有变量和私有函数
        var privateVariable = 10;
        function privateFunction(){
            return false;
        }
    
        //特权/公有 方法和属性
        return {
            publicProperty: true,
            publicMethod : function(){
                    privateVariable++;
                    return privateFunction();
            }
        }
     })();
    这个模块模式使用了一个返回对象的匿名函数。匿名函数内部,定义了私有变量和函数。返回的对象 字面量中只包含可以公开的属性和方法。由于这个对象是在匿名函数内部定义的,因此它的公有方法 有权 访问私有变量和函数。这个
    对象字面量定义的是单例的公共接口。这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的:
     var application = (function(){
        //私有变量和函数
        var components = new Array();
        components.push(new BaseComponent());
        //公有
        return{
            getComponent:function(){
                return components.length;
            },
            registerComponent:function(component){
                if(typeof component == "object")
                {
                    components.push(component);
                }
            }
        }
     })();

    如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。以这种模式创建的每个单例都是 Object 的实例,

    增强的模块模式:

    这种增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况。

    var singleton = (function(){
        //私有变量和私有函数
        var privateVariable = 10;
        function privateFunction(){
            return false;
        }
        //创建对象
        var object = new CustomType();
        //添加特权/公有属性和方法
        object.publicProperty = true;
        object.publicMethod = function(){
            privateVariable++;
            return privateFunction();
        };
        //返回这个对象
        return object;
    })();

    总结:

    函数表达式不同于函数声明。函数声明要求有名字,但函数表达式不需要。没有名字的函数表达式也叫做匿名函数。
    递归函数应该始终使用 arguments.callee 来递归地调用自身,不要使用函数名——函数名可能会发生变化。


    当在函数内部定义了其他函数时,就创建了闭包。闭包有权访问包含函数内部的所有变量,原理:

    闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域。

    函数的作用域及其所有变量都会在函数执行结束后被销毁。

    当函数返回了一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止。使用匿名函数可以在 JavaScript 中模仿块级作用域(JavaScript 本身没有块级作用域的概念),要点如下。

     创建并立即调用一个函数,这样既可以执行其中的代码,又不会在内存中留下对该函数的引用。
     结果就是函数内部的所有变量都会被立即销毁—

    闭包还可以用于在对象中创建私有变量,相关概念和要点如下。
     即使 JavaScript 中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量。
     有权访问私有变量的公有方法叫做特权方法。
     可以使用构造函数模式、原型模式来实现自定义类型的特权方法,也可以使用模块模式、增强的模块模式来实现单例的特权方法。

    JavaScript 中的函数表达式和闭包都是极其有用的特性,利用它们可以实现很多功能。因为创建闭包必须维护额外的作用域,所以过度使用它们可能会占用大量内存。

     


  • 相关阅读:
    自定义的事件管理器
    解决修改表结构,添加外键时出现“约束冲突”的错误
    jQuery学习(二) 自定义扩展函数
    iBt(001-004)原文与试译
    老婆怀孕了!(5+6)
    Mac_如何打开系统文件library
    Mac_如何通过命令行装包到ios手机
    MAC干净卸载pycharm
    selenium自动化_如何启动safari浏览器
    selenium自动化_click方法点击无效
  • 原文地址:https://www.cnblogs.com/a-lonely-wolf/p/5621935.html
Copyright © 2011-2022 走看看