前端面试基础必备JS学习笔记

常用的Javascript设计模式

2018-09-09  本文已影响4人  puxiaotaoc
一、构造函数模式
// 构造一个动物的函数
  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中的几种设计模式

上一篇下一篇

猜你喜欢

热点阅读