# 前端中常用的设计模式

为解决统一问题,而设计不同的解决方案,达到最高效,优雅,简洁。
各个方法中,高质量的代码都有相似性,这个相似性就是用”模式“代指。

  1. 单例模式
  2. 代理模式
  3. 命令模式
  4. 发布-订阅模式
  5. 职责链模式

base64 减少请求数
雪碧图 两个请求 css png

<!-- index.html -->
<script type="module" src="./index.js"></script>
// index.js
import a from "a.js";
a.init();
// a.js
export default {
  init() {
    console.log("this is a.init");
  },
};

# 单例模式

js 方法大多都是单例模式。
保证一个类只有一个实例,并提高一个访问它全局访问点,如 window 对象。 当我们点击登录按钮的时候页面出现一个登录浮窗,这个浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,这个登录的浮窗就适合用单例模式创建。

var CreateDiv = (function() {
  var instance;
  var CreateDiv = function(html) {
    if (instance) {
      return instance;
    }
    this.html = html;
    this.init();
    return (instance = this);
  };
  CreateDiv.prototype.init = function() {
    var div = document.createElement("div");
    div.innerHTML = this.html;
    document.body.appendChild(div);
    div = null;
  };
  return CreateDiv;
})();

var a = new CreateDiv("a");
var b = new CreateDiv("b");
alert(a == b); // true
// 1. CreateDiv使用类似于传统的面向对象编程
// 2. CreateDiv负责维护单例
// 3. 闭包
(function() {
  var nameSpace = {
    init: function(argument) {
      var me = this;
      me.render();
      me.bind();
    },
    render: function(argument) {
      var me = this;
      me.btn = $("$test");
    },
    bind: function(argument) {
      var me = this;
      me.btn.on("focus", $.proxy(me._clearInput, this));
    },
  };
  // 单例模式
  module.exports = nameSpace;
})();
nameSpace.init();

var _instance = null;
class Car {
  constructor() {
    if (!_instance) {
      _instance = this;
    }
    return _instance;
  }
}
let car1 = new Car();
let car2 = new Car();
console.log(car1 === car2);

# 代理模式

代理拥有的能力得依靠本体 为一个对象提供一个代用品或占位符,以便控制对它的访问,这个代用品或占位符,就是代理。 当客户不方便直接访问一个对象或不满足需要的时候,提供一个替身对象控制这个对象的访问,替身对象对请求做一些处理后,在把处理后的数据交给本体对象。

/** demo01 */
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(src);
      img.src = src;
    },
  };
})();
proxyImage.setSrc("");

/** demo02 */
// proxy.js
var canvas = require("");
var image = require("");
var draw;
if(){
  draw = canvas.draw();
} else {
  draw = image.draw();
}
exports {
  draw
}

// index.js
var image = requier("./proxy.js");
image.draw();

/** demo3 es6 */
class Real {
  doSth(){
    console.log("doSth");
  }
}
class Proxy extends Real {
  constructor(){
    super();
  }
  doSth(){
    setTimeOut(function(){
      super.doSth();
    },2000)
  }
}
new Proxy().doSth();
interface Subject {
  void request();
}
class RealSubject implements Subject {
  public void request(){
    System.out.printIn("RealSubject");
  }
}
class Proxy implements Subject {
  private Subject subject;
  public Proxy(Subject subject){
    this.subject = subject;
  }
  public void request(){
    System.out.println("begin");
    subject.request();
    System.out.println("end");
  }
}
public class ProxyTest{
  public static void main(String args[]){
    RealSubject subject = new RealSubject();
    Proxy p = new Proxy(subject);
    p.request();
  }
}

# 命令模式

命令模式中的命令指的是一个执行某些特定事情的指令,有时候需要向某些特定事情的指令。
常见的应用场景有时候需要向某些对象发送请求,但并不知道请求的接受者是谁,也不知道被请求的操作是什么。假如我们去快餐店,我们可以点餐取消但是我们并不用关心厨师是谁,怎么做

// if else
// makeCommand=>发送命令   reciver=> 接收   state => 状态
var makeCommand = function(reciver, state) {
  return function(argument) {
    receiver[state]();
  };
};
var Ryu = {
  attack: function() {
    console.log("攻击");
  },
  defense: function() {
    console.log("防御");
  },
  crouch: function() {
    console.log("蹲下");
  },
};
var command = makeCommand(Ryu, "attack");
command();
// tips: 如果实现撤销操作,我们可以将每一步操作存储到历史堆栈中,然后依次循环执行这些命令的undo操作,直到全部完成。

class CarManager {
  requestInfo(model, id) {
    return model + "***" + id;
  }
  doSth(model, id) {
    return model + "*** dosth ***" + id;
  }
  // 静态方法
  static execute(name) {
    let carManage = new CarManager();
    return carManage[name].apply(carManage, Array.from(arguments));
  }
}
carManager.execute("requestInfo", "red", 3);

# 发布订阅模式

  • 发布订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都将得到通知。
    现实生活中,如果我们去售楼中心服务人员 A 接待,然后在有客户找到 A,这个时候暂时没房了,等到有房的时候不可能服务人员挨个打电话通知,而是订阅 A 的公共提醒,获取信息。
  • 观察者使用场景:当一个对象的改变需要同时改变其他对象,并不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式
  • 观察者模式所做的工作是解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一半的变化。
// 通用代码
var observer = {
  // 订阅
  addSubscriber: function(callback) {
    this.subscribers[this.subscribers.length] = callback;
  },
  removeSubscriber: function(callback) {
    for (var i = 0; i < this.subscribers.length; i++) {
      if (this.subscribers[i] == callback) {
        delete this.subscribers[i];
      }
    }
  },
  publish: function(what) {
    for (var i = 0; i < this.subscribers.length; i++) {
      if (typeof this.subscribers[i] === "function") {
        this.subscribers[i](what);
      }
    }
  },
  // 将对象o具有观察者功能
  make: function(o) {
    for (var i in this) {
      o[i] = this[i];
      o.subscribers = [];
    }
  },
};
/// 订阅2个对象 blogger和user,使用observer.make方法将这2个对象具有观察者功能
var blogger = {
  recommend: function(id) {
    var msg = "dudu 推荐了帖子: " + id;
    this.publish(msg);
  },
};
var user = {
  vote: function(id) {
    var msg = "有人投票了!ID=" + id;
    this.publish(msg);
  },
};
observer.make(blogger); // blogger是一个新的观察者
observer.make(user);
// 使用
var tom = {
  read: function(what) {
    console.log("Tom看到如下信息:" + what);
  },
};
var mm = {
  show: function(what) {
    console.log("mm看到如下信息:" + what);
  },
};

// 订阅
blogger.addSubscriber(tom.read);
blogger.addSubscriber(mm.read);
/// 调用发布
blogger.recommend(123);

// 退订
blogger.removeSubscriber(mm.show);
// 调用发布
blogger.recommend(3412);
// 另一个对象订阅
user.addSubscriber(mm.show);
user.vote(789); // 调用发布
// listener方法
listener.on("/test/p", "say", function(data, info) {
  console.log(info);
});
listener.trigger("/test/p", "say", "我是信息");

# 1. Vue 内部的订阅发布机制

vm.$on;
vm.$once;
vm.$off;
vm.$emit;

# 2. jQuery 三大事件

// on off trigger
// jquery
(functionn($){
  var o=$({});
  $.subscrible = function(){
    o.on.apply(o, arguments);
  };
  $.unsubscribe=function(){
    o.off.apply(o, arguments);
  }
  $.public = function(){
    o.trigger.apply(o, arguments);
  }
}(jQuery))
// 调用方法
$.subscribe("/some/topic", function(e, a, b,c){
  console.log(a+b+c);
});
$.publish("/some/topic", ["a","b","c"]);
$.unsubscribe("/some/topic", handle);

# 3. Nodejs 订阅机制

var EventEmitter = require("events").EventEmitter;
var event = new EventEmitter();
event.on("some_event", function() {
  console.log("some_event 事件触发");
});
setTimeout(function() {
  event.emit("some_event");
}, 1000);

# 职责链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些关系连接成一条链,并沿这条链传递该请求,直到一个对象处理它为止。
现实生活中,如我们做公交,人太多,我们先挤上公交没刷卡怎么办,我们可以把公交车卡交给前面的人不停的传递,直到刷卡结束。

var fn1 = function(data) {
  if (data == 1) {
    console.log("fn1->", data);
  } else {
    return "next";
  }
};
var fn2 = function(data) {
  if (data == 2) {
    console.log("fn2->", data);
  } else {
    return "next";
  }
};
var fn3 = function(data) {
  if (data == 3) {
    console.log("fn3->", data);
  } else {
    return "next";
  }
};
// 业务逻辑归整,拉好坑,fn填坑
Function.prototype.after = function(fn) {
  var self = this;
  return function() {
    var ret = self.apply(this, arguments);
    if (ret === "next") {
      return fn.apply(this, arguments);
    }
    return ret;
  };
};
var order = fn1.after(fn2).after(fn3);

order(1);
module.exports = function(router) {
  router.get("/:id.html", newsCommentsList, detail, show);
};
// mid 层
module.exports = {
  newsCommentsList: function(req, res, next) {
    sefeRequest({
      success: function(error, response, body) {
        if (!error) {
          next();
        }
      },
      error: function() {
        res.render("index/index.tpl", {});
      },
    });
  },
  // controller
  show: function(req, res, next) {
    res.render("index/index.tpl", {});
  },
};

# 策略模式

// 表单
const firstname = "123";
class Checker {
  constructor(check, info) {
    [this.check, this.info] = [check, info];
  }
}
class Validator {
  constructor(config) {
    this.config = config;
  }
  validate() {
    // config 循环
    // 0=>[0, 1] Validator[0] = Checker[1]
  }
}
Validator.isNumber = new Checker((val) => !isNaN(val), "必须实施数");
Validator.isNotEmpty = new Checker((val) => !="", "必须不为空");

let config = [["firstname", "isNumber"],["firstname", "isNotEmpty"]];
const validator = new Validator(config);
Validator.validate(config)