# 前端中常用的设计模式
为解决统一问题,而设计不同的解决方案,达到最高效,优雅,简洁。
各个方法中,高质量的代码都有相似性,这个相似性就是用”模式“代指。
- 单例模式
- 代理模式
- 命令模式
- 发布-订阅模式
- 职责链模式
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)