常用的Javascript设计模式

作者: puxiaotaoc | 来源:发表于2018-09-09 14:21 被阅读4次
    一、构造函数模式
    // 构造一个动物的函数
      function Animal(name,color){
        this.name = name;
        this.color = color;
        this.getName = function(){
          return this.name;
        }
      }
      // 实例一个对象
      var cat = new Animal('大毛','white');
      console.log(cat.getName()); // 大毛
    
    二、工厂模式
    function Animal(name,color){
        var o = new Object();
        o.name = name;
        o.color = color;
        o.getName = function(){
          return this.name;
        };
        return o;
      }
    
      var cat = new Animal('大毛','white');
      console.log(cat.getName()); // 大毛
    
    三、模块模式
      var Car = (function(){
        var name = '大毛';
        function getName(){
          console.log(name);
        }
        function getColor(color){
          console.log(color);
        }
        return {
          name: getName,
          color: getColor
        }
      })();
      Car.name(); // 大毛
      Car.color('red'); // red
    
    四、混合模式
    // 混合模式 = 原型模式 + 构造函数模式
      function create(parentObj) {
        function F() {}
        F.prototype = parentObj;
        return new F();
      }
    
      function Animal(name, color) {
        this.name = name;
        this.color = color;
      }
    
      Animal.prototype.getName = function() {
        return this.name;
      }
    
      function Cat(name, color) {
        Animal.call(null, name, color);
        this.color = color;
      }
    
      Cat.prototype = create(Animal.prototype);
    
      Cat.prototype.getColor = function() {
        return this.color;
      }
      var cat = new Cat('大毛', 'white');
      console.log(cat.getColor()) // white
    
    五、单例模式
    var Single = (function() {
        var instance;
        function init() {
          // 生成单例的构造函数的代码
          return {};
        }
        return {
          // 如果该实例存在,则直接返回,否则就对其实例化
          getInstance: function() {
            if (!instance) {
              instance = init();
            }
            return instance;
          }
        }
      })();
    
      var obj1 = Single.getInstance();
      var obj2 = Single.getInstance();
      console.log(obj1 === obj2); // true
    
    // 保证一个类仅有一个实例,并提供一个访问它的全局访问点,例如:线程池,全局缓存,登录浮窗
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <script src="../my_ajax.js"></script>
    </head>
    
    <body>
      <form action="">
        <input type="text" name="user">
        <input type="password" name="pwd">
        <input type="submit" value="登录">
    </form>
        <script type="text/javascript">
          var submitObj = {
            form: document.forms[0],
            submitUrl: "data2.php",
            _init: function() {
              this.handSubmit();
            },
            handSubmit: function() {
              var that = this;
              this.form.onsubmit = function(e) {
                e.preventDefault(); //阻止表单提交默认行为
                if (!that.checkForm()) return;
                that.ajaxSubmit();
              }
            },
            checkForm: function() {
              return true; //可使用正则表达式验证
            },
            ajaxSubmit: function() {
              my_ajax.post(this.submitUrl, new FormData(this.form), this.submitResult)
            },
            submitResult: function(result) {
              console.log(result);
            }
          }
          submitObj._init();
        </script>
    </body>
    
    </html>
    
    // 需要把单例的逻辑代码单独提取,然后使用惰性单例的方式,也就是返回方法;
    // 只有在点击的时候,才会进行执行,javascript的单例;
    // 跟类不一样,无需创建多余的构造函数这些,直接创建全局变量即可;
    !(function() {
            //管理单例的逻辑代码,如果没有数据则创建,有数据则返回
            var getSingle = function(fn) { //参数为创建对象的方法
              var result;
              return function() { //判断是Null或赋值
                return result || (result = fn.apply(this, arguments));
              };
            };
            //创建登录窗口方法
            var createLoginLayer = function() {
              var div = document.createElement('div');
              div.innerHTML = '我是登录浮窗';
              div.style.display = 'none';
              document.body.appendChild(div);
              return div;
            };
            //单例方法
            var createSingleLoginLayer = getSingle(createLoginLayer);
    
            //使用惰性单例,进行创建
            document.getElementById('loginBtn').onclick = function() {
              var loginLayer = createSingleLoginLayer();
              loginLayer.style.display = 'block';
            };
          })()
    

    六、发布订阅者模式

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    
    <body>
      <button id="btn1">按钮一</button>
      <button id="btn2">按钮二</button>
      <script type="text/javascript">
        var ObserverEvent = (function() {
          var clientList = [],listen, trigger, remove;
          listen = function(key, fn) {
            if (!clientList[key]) {
              clientList[key] = [];
            }
            clientList[key].push(fn);
          };
          trigger = function() {
            var key = Array.prototype.shift.call(arguments),
              fns = clientList[key];
            if (!fns || fns.length === 0) {
              return false;
            }
            for (var i = 0, fn; fn = fns[i++];) {
              fn.apply(this, arguments);
            }
          };
          remove = function(key, fn) {
            var fns = clientList[key];
            if (!fns) {
              return false;
            }
            if (!fn) {
              fns && (fns.length = 0);
            } else {
              for (var l = fns.length - 1; l >= 0; l--) {
                var _fn = fns[l];
                if (_fn === fn) {
                  fns.splice(l, 1);
                }
              }
            }
          };
          return {
            listen: listen,
            trigger: trigger,
            remove: remove
          }
        })();
        ObserverEvent.listen('squareMeter88', fn1 = function(price) {
          console.log('价格=' + price);
        });
        ObserverEvent.listen('squareMeter100', function(price) {
          console.log('价格=' + price);
        });
        ObserverEvent.trigger('squareMeter88', 200000);
        ObserverEvent.trigger('squareMeter100', 300000);
        ObserverEvent.remove('squareMeter88', fn1);
        ObserverEvent.trigger('squareMeter88', 200000);
      </script>
    </body>
    
    </html>
    
    var EventCenter = (function() {
          var events = {};
    
          // 绑定事件 添加回调
          function on(evt, handler) {
            events[evt] = events[evt] || [];
            events[evt].push({
              handler: handler
            })
          }
    
          function fire(evt, arg) {
            if (!events[evt]) {
              return
            }
            for (var i = 0; i < events[evt].length; i++) {
              events[evt][i].handler(arg);
            }
          }
    
          function off(evt) {
            delete events[evt];
          }
          return {
            on: on,
            fire: fire,
            off: off
          }
        }());
    
        var number = 1;
        EventCenter.on('click', function(data) {
          console.log('click 事件' + data + number++ + '次');
        });
        EventCenter.off('click'); //  只绑定一次
        EventCenter.on('click', function(data) {
          console.log('click 事件' + data + number++ + '次');
        });
    
        EventCenter.fire('click', '绑定');
    

    七、适配器模式
           适配器模式(Adapter)是将一个类(对象)的接口(方法或属性)转化成客户希望的另外一个接口(方法或属性),适配器模式使得原本由于接口不兼容而不能一起工作的那些类(对象)可以一些工作,俗称包装器(wrapper);

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="../my_ajax.js"></script>
    </head>
    <body>
    <p id="p1"></p>
    <p id="p2"></p>
    ​
    <script type="text/javascript">
        //适配器模式:在不修改旧的模式的前提下,来适应新的变化
        my_ajax.get("data3.json",function (result) {
            showMsg(JSON.parse(result),p1);
        })
        my_ajax.get("data4.json",function (result) {
            showMsgAdapter(JSON.parse(result),p2);
        })
        function showMsg(obj,p) {
            p.innerHTML = obj.name;
        }
        function showMsgAdapter(arr,p) {
            showMsg(arr[0],p2);
        }
    </script>
    </body>
    </html>
    
    八、代理模式

           当客户不方便直接访问一个对象的时候,需要提供一个替身对象来控制对这个对象的访问,即把对一个对象的访问, 交给另一个代理对象来操作,代理模式分为:虚拟代理和保护代理:

    • 虚拟代理:把一些开销很大的对象,延迟到真正需要它的时候才去创建;
    • 保护代理:用于控制不同权限的对象对目标对象的访问;
    图片预加载

           使用虚拟代理可以完成图片预加载功能,先用一张loading图片占位,然后用异步方式加载图片,等图片加载完毕后填充到img节点里;

    // javascript事件均为异步事件,当执行proxyImage时,会先设置loading.gif;
    // 等图片加载完毕后,会执行myImage操作;
          var myImage = (function() {
            var imgNode = document.createElement('img');
            document.body.appendChild(imgNode);
            return {
              setSrc: function(src) {
                imgNode.src = src;
              }
            };
          })();
          //预加载方法
          var proxyImage = (function() {
            var img = new Image();
            img.onload = function() {
              myImage.setSrc(this.src);
            }
            return {
              setSrc: function(src) {
                myImage.setSrc("loading.gif");
                img.src = src;
              }
            };
          })();
          proxyImage.setSrc('实际图片.jpg'); //预加载
          myImage.setSrc('实际图片'.jpg); //普通加载
    // 加载方法和预加载方法,必须使用立即执行函数,否则setSrc方法调用不到;
    // 如果不使用代理模式,会执行加载图片和预加载操作,当我们不需要预加载功能时,无法进行快速隔离
    // 这种懒加载方法不用代理模式也可以实现,代理模式可以让 myImage 只做一件事,即只负责将实际图片加入到页面中;
    // 而loading图片交给proxyImage去做,从而降低代码的耦合度;
    // 当不想用loading的时候,可以直接调用 myImage 方法,即不需要代理对象的话,直接可以换成本体对象调用该方法即可;
    

    参考链接:
    常见的6种JavaScript设计模式
    Javascript设计模式
    常用的Javascript设计模式
    js中的几种设计模式

    相关文章

      网友评论

        本文标题:常用的Javascript设计模式

        本文链接:https://www.haomeiwen.com/subject/ezhrgftx.html