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());

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

  • 相关阅读:
    Windows server 2016 解决“无法完成域加入,原因是试图加入的域的SID与本计算机的SID相同。”
    Windows Server 2016 辅助域控制器搭建
    Windows Server 2016 主域控制器搭建
    Net Framework 4.7.2 覆盖 Net Framework 4.5 解决办法
    SQL SERVER 2012更改默认的端口号为1772
    Windows下彻底卸载删除SQL Serever2012
    在Windows Server2016中安装SQL Server2016
    SQL Server 创建索引
    C#控制台或应用程序中两个多个Main()方法的设置
    Icon cache rebuilding with Delphi(Delphi 清除Windows 图标缓存源代码)
  • 原文地址:https://www.cnblogs.com/LO-ME/p/3602540.html
Copyright © 2011-2022 走看看