zoukankan      html  css  js  c++  java
  • JavaScript 函数闭包的应用

    闭包的应用: 定义JS模块

    具有特定功能的js文件,将所有的数据和功能都封装在一个函数内部(私有的)

    只向外暴露一个包信n个方法的对象或函数,模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能

    // 自定义模块
    
    function coolModule() {
      // 私有的数据
      var msg = 'atguigu'
      var names = ['I', 'Love', 'you']
    
      //私有的操作数据的函数
      function doSomething() {
        console.log(msg.toUpperCase())
      }
      function doOtherthing() {
        console.log(names.join(' '))
      }
    
      //向外暴露包含多个方法的对象,如果一个方法就直接将这个方法return即可
      return {
        doSomething: doSomething,
        doOtherthing: doOtherthing
      }
    }
    <body>
    <script type="text/javascript" src="05_coolModule.js"></script>
    <script type="text/javascript">
      var module = coolModule()
      module.doSomething()
      module.doOtherthing()
    </script>
    </body>
    // 自定义模块2
    
    (function (window) {
      //私有的数据
      var msg = 'atguigu'
      var names = ['I', 'Love', 'you']
      //操作数据的函数
      function a() {
        console.log(msg.toUpperCase())
      }
      function b() {
        console.log(names.join(' '))
      }
    
      window.coolModule2 =  {
        doSomething: a,
        doOtherthing: b
      }
    })(window)
    <body>
    <script type="text/javascript" src="05_coolModule2.js"></script>
    <script type="text/javascript">
      coolModule2.doSomething()
      coolModule2.doOtherthing()
    </script>
    </body>

    模仿块级作用域

    JavaScript 没有块级作用域的概念,那么可以模拟像java中将很多变量私有化封装起来,保护数据,防止数据泄漏,封装细节,这样安全性和可控性更高

      function box(count) {
            for (var i=0; i<count; i++) {     //块级作用域(JS中没有这个东西)
            }
            alert(i); //i 不会因为离开了 for 块就失效
        }
        box(2);    //结果是2,还是可以访问的
      function box(count) {
            for (var i=0; i<count; i++) {     //块级作用域(JS中没有这个东西)
            }
            var i;             //就算重新声明,也不会前面的值
            alert(i); //i 不会因为离开了 for 块就失效
        }
        box(2);    //结果是2,还是可以访问的

    以上两个例子,说明 JavaScript 没有块级语句的作用域,if () {} for () {}等没有作用域,如果有,出了这个范围 i 就应该被销毁了。就算重新声明同一个变量也不会改变它的值。

    JavaScript 不会提醒你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(如果初始化了,当然还会执行的)。使用模仿块级作用域可避免这个问题。

    模仿块级作用域(私有作用域)

    /*
        (function () {
            //这里是块级作用域
        })();
    */
        function box(count) {
            (function () {
                for (var i = 0; i<count; i++) {
                    alert(i);//只能在这里i才是有效的才是能访问的
                }
            })();
            alert(i); //这里i已经出了作用域   报错,无法访问
        }
        box(2);

    使用了块级作用域(私有作用域)后,匿名函数中定义的任何变量,都会在执行结束时被销毁。这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。

    一般来说,我们都应该尽可能少向全局作用域中添加变量和函数。在大型项目中,多人开发的时候,过多的全局变量和函数很容易导致命名冲突,引起灾难性的后果。

    如果采用块级作用域(私有作用域),每个开发者既可以使用自己的变量,又不必担心搞乱全局作用域。

        (function () {
            var box = [1,2,3,4];
            alert(box);     
        })();
        alert(box);//box 出来就不认识了

    在全局作用域中使用块级作用域可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。

    私有变量

    JavaScript 没有私有属性的概念;所有的对象属性都是公有的。不过,却有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。

    function box() {
      var age = 100; //私有变量,外部无法访问,只能返回
    }

    而通过函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量。而利用这一点,可以创建用于访问私有变量的公有方法。

      function Box(){
            this.age = 100;                    //可以访问,公有的
            this.run = function(){        //可以访问,公有的
                return "运行中....";
            }
        }
        var box = new Box();
        alert(box.age);
        alert(box.run());
        function Box() {
            var age = 100;                         //私有变量
            
            function run() {                     //私有函数
                return '运行中...';
            }
            
            this.get = function () {             //对外公共的特权方法
                return age + run();
            };
        }
        var box = new Box();
        alert(box.get());

    可以通过构造方法传参来访问私有变量。

        function Person(value) {
            var user = value; //这句其实可以省略
            this.getUser = function () {
                return user;
            };
            this.setUser = function (value) {
                user = value;
            };
        }
        var box = new Person("hua");
        alert(box.getUser());
        box.setUser(4);
        alert(box.getUser());

    但是对象的方法,在多次调用的时候,会多次创建。

      function Person(value) {
            var user = value; //这句其实可以省略
            this.getUser = function () {
                return user;
            };
            this.setUser = function (value) {
                user = value;
            };
        }
        var box = new Person("hua");
        alert(box.getUser());
        var box2 = new Person("kkk");
        alert(box.getUser());//结果还是hua,说明setUser方法没有共享

    可以使用静态私有变量来避免这个问题。

     



    静态私有变量

    通过块级作用域(私有作用域)中定义私有变量或函数,同样可以创建对外公共的特权方法。

      (function () {
            var age = 100;                //私有变量
            function run() {
                return '运行中...';
            }
            //function Box(){}                //构造方法,在函数里写构造函数不支持,因为私有作用域里的函数,外部无法访问,所以要做成全局的,如下
            Box = function (value){            //全局构造方法
                age = value;
            }; 
            Box.prototype.getAge = function () {         //原型方法,只生成一个地址
                return age + run();
            };
            Box.prototype.setAge = function(value){
                age = value;
            }
        })();
        
        var box1 = new Box(23);
        alert(box1.getAge());
        var box2 = new Box(43);
        alert(box2.getAge());
        box2.setAge(76);
        alert(box1.getAge());//使用box2设置了age的值,再使用box1获取age的时候变成了box2设置的值,说明共享了

    使用了 prototype 导致方法共享了,而 age也就变成静态属性了。(所谓静态属性,即共享于不同对象中的属性)。




    模块模式

    之前采用的都是构造函数的方式来创建私有变量和特权方法。那么对象字面量方式就采用模块模式来创建。

    var box = function () {
            var age = 100;
            function run() {
                return '运行中...';
            }
            return { //直接返回对象
                go : function () {
                    return age + run();
                }
            };
        }();
        
        //上面的直接返回对象的例子,也可以这么写:
        var box = function () {
            var age = 100;
            function run() {
                return '运行中...';
            }
            var obj = { //创建字面量对象
                go : function () {
                    return age + run();
                }
            };
            return obj; //返回这个对象
        }();

    字面量的对象声明,其实在设计模式中可以看作是一种单例模式,所谓单例模式,就是永远保持对象的一个实例。

      function Desk() {};
            var box = function () {
            var age = 100;
            function run() {
                return '运行中...';
            }
            var desk = new Desk(); //可以实例化特定的对象
            desk.go = function () {
                return age + run();
            };
            return desk;
        }();
        alert(box.go());

    增强的模块模式,这种模式适合返回自定义对象,也就是构造函数。

  • 相关阅读:
    Linux下使用cut切割有规则的列文本
    注解相关
    修改Feign数据解析,由jackson改为fastjson,同时解决fastjson中Content-Type问题
    Spring Data JPA整合REST客户端Feign时: 分页查询的反序列化报错的问题
    Aliyun STS Java SDK示例
    GIT : IDEA切换到某个tag
    [LeetCode] 351. Android Unlock Patterns 安卓解锁模式
    QSpinBox 和 QSlider 联合使用方法
    Qt 控件随窗口缩放
    [LeetCode] 350. Intersection of Two Arrays II 两个数组相交之二
  • 原文地址:https://www.cnblogs.com/LO-ME/p/3602540.html
Copyright © 2011-2022 走看看