JS设计模式有很多种
常见的设计模式
(1): 单例模式(Singleton Pattern)-创建
1: 第一种
// 单例模式(Singleton Pattern),后端用的比较多
// 单例模式创建多少对象就只有一个实例
var SinglePattern = (function(){
function Single(name) {
this.name = name;
}
return function(name) {
if(!Single.instance) {
Single.instance = new Single(name)
}
return Single.instance
}
})()
var obj1 = new SinglePattern("张三")
var obj2 = new SinglePattern("李四")
2:第二种
var SinglePattern = (function(){
var instance = null;
function Single(name) {
this.name = name;
}
return function(name) {
if(!instance) {
instance = new Single(name)
}
return instance
}
})()
var obj1 = new SinglePattern("张三")
var obj2 = new SinglePattern("李四")
console.log("obj1========",obj1);
console.log("obj2========",obj2);
console.log(obj1 === obj2);
应用场景:数据一致性
(2): 工厂模式(Factory Pattern)- 创建
1: 第一种
function School(type) {
var instance = new Object();
instance.name = type.name;
instance.age = type.age;
instance.gender = type.gender;
return instance;
}
var student = School({name: '学生',age: 10,gender: '男'})
var teacher = School({name: '老师',age: 23,gender: '女'})
console.log("student",student);
console.log("teacher",teacher);
2:第二种
// 历史
function History(d) { return this.data = d };
// 数学
function Math(d) { return this.data = d };
// 课程工厂
function FactoryPattern(type) {
var Clazz = null;
var data = null;
switch(type) {
case "history":
Clazz = History;
data = "历史"
break;
case "math":
Clazz = Math;
data = '数学';
break;
}
return new Clazz(data)
}
var c1 = FactoryPattern("history")
var c2 = FactoryPattern("math")
console.log(c1,c2);
工厂模式应用场景:工厂模式-创建(批量生产对象)
(3): 观察者模式(Observer Pattern)-行为
1: ES5写法
// 依赖收集器
function Dep() {
this.subs = [] // 数据存储,存储需要订阅的内容,未来方便通知
}
Dep.prototype.addSub = function(sub) {
this.subs.push(sub)
}
// 事件触发方:通知的行为
Dep.prototype.notify = function() {
this.subs.forEach(sub => {
sub.update()
});
}
// 观察者模式
function Observer(name) {
this.name = name;
}
Observer.prototype.update = function() {
console.log(this.name,"检测到了更新");
}
var obj1 = new Observer("小a")
var obj2 = new Observer("小b")
// 创建主题对象,对主题产生订阅
var dep = new Dep()
dep.addSub(obj1)
dep.addSub(obj2)
dep.notify()
2: ES6写法
// 观察者模式
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log(this.name + "更新了")
}
}
// 依赖收集器
class Dep {
constructor() {
this.subs = []
}
addSubs(watcher) {
this.subs.push(watcher)
}
notify() {
this.subs.forEach(w => {
w.update()
})
}
}
var obj1 = new Observer("小a")
var obj2 = new Observer("小b")
var dep = new Dep()
dep.addSubs(obj1)
dep.addSubs(obj2)
dep.notify()
Observer 负责视图更新,Dep负责收集依赖和通知,视图和逻辑分离,具备消息订阅功能
应用场景: 消息订阅
附加:手写一个eventsBus
class Bus {
constructor(){
this.handlers = {}
}
// 实现事件订阅on
on(name,callback) {
// 没有就新增
if(!this.handlers[name]) {
this.handlers[name] = []
}
// 有的话就进行push
this.handlers[name].push(callback)
}
// 实现事件发布emit
emit(name,...args) {
if(this.handlers[name]) {
this.handlers[name].forEach(cb=>{
cb(...args)
})
}
}
// 实现事件的销毁off
off(name,callback) {
const callbacks = this.handlers[name]
const index = callbacks.indexOf(callback)
if(index!=-1) {
callbacks.splice(index,1)
delete this.handlers[name]
}
console.log("this.handlers",this.handlers);
}
}
var $bus = new Bus();
var fn1 = function(e) {
console.log("e",e);
}
var fn2 = function(e) {
console.log("e",e);
}
$bus.on("sendMessage",fn1)
$bus.emit("sendMessage",{id:1,name:"张三"})
$bus.off("sendMessage",fn1)
$bus.on("sendInfo",fn2)
$bus.emit("sendInfo","你好呀!")
$bus.off("sendInfo",fn2)
(4) 装饰器模式(Decorator Pattern)-结构
// Decorator Pattern 装饰器模式
function Coffee() {
this.info = "苦咖啡";
}
Coffee.prototype.show = function() {
console.log(this.info); // 这个this就是构造函数本身
}
// 装饰器
function CoffeeDecorator(coffee) {
this.coffee = coffee; // 保存被装饰的对象
}
// 加工外部操作,更加利于加工类的扩展和维护
CoffeeDecorator.prototype.show = function() {
console.log("糖是甜的呦!");
this.coffee.show()
}
var co1 = new Coffee();
var coDc = new CoffeeDecorator(co1)
coDc.show()
(5) 代理模式(Proxy-Pattern)
// 创建代理类
function Coffee() {
this.info = "苦咖啡"; // 代码不利于维护
}
Coffee.prototype.show = function() {
console.log(this.info);
}
// 创建代理类
function CoffeeProxy() {
// 用代理替换文本 不存在本体
this.coffee = new Coffee()
}
CoffeeProxy.prototype.show = function() {
console.log("代理的甜");
this.coffee.show()
}
var cp1 = new CoffeeProxy()
cp1.show()
其实装饰器模式和代理模式很相似,如何选择呢,根据当前是否有装饰着实例,如果没有,就用代理模式创建一个实例
(6): 策略模式(Strategy-Pattern)-行为
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form action="" id="form">
<input type="text" id="username" placeholder="用户名" /><br />
<input type="text" id="password1" placeholder="密码" /><br />
<input type="text" id="password2" placeholder="确认密码" /><br />
<input type="text" id="phone" placeholder="电话" /><br />
<button>提交</button>
</form>
<script>
function Validate() {}
// 四个功能函数, 通过外部 调用rules[名称]获取
Validate.prototype.rules = {
// 是否手机号
isMobile: function (str) {
var rule = /^1[3,4,5,7,8,9][0-9]\d{8}$/;
return rule.test(str);
},
// 是否必填
isRequired: function (str) {
// 除去首尾空格
var value = str.replace(/(^\s*)|(\s*$)/g, "");
return value !== "";
},
// 最小长度
minLength: function (str, length) {
var strLength = str.length;
return strLength >= length;
},
// 是否相等
isEqual: function () {
// 可以接收多个参数比较
var args = Array.prototype.slice.call(arguments);
// 取首项与后面所有的项比较,如果每个都相等,就返回true
var equal = args.every(function (value) {
return value === args[0];
});
return equal;
},
};
Validate.prototype.test = function (rules) {
var v = this;
var valid; // 保存校验结果
// 外部传递的规则配置参数rules
for (var key in rules) {
// 遍历校验规则对象
// 数组
for (var i = 0; i < rules[key].length; i++) {
// 遍历每一个字段的校验规则
var ruleName = rules[key][i].rule; // 获取每一个校验规则的规则名
var value = rules[key][i].value; // 获取每一个校验规则的校验值
if (!Array.isArray(value)) {
// 统一校验值为数组类型
value = new Array(value);
}
// ruleName // 确保this是validate对象
// [this.password2.value, 6] || this.password2.value
var result = v.rules[ruleName].apply(v, value); // 调用校验规则方法进行校验
if (!result) {
// 如果校验不通过,就获取校验结果信息,并立即跳出循环不再执行,节约消耗
valid = {
errValue: key,
errMsg: rules[key][i].message,
};
break;
}
}
if (valid) {
// 如果有了校验结果,代表存在不通过的字段,则立即停止循环,节约消耗
break;
}
}
return valid; // 把校验结果反悔出去
};
var formData = document.getElementById("form");
formData.onsubmit = function () {
event.preventDefault();
var validator = new Validate();
// 验证规则 抽离出来, 便于更为灵活的应用验证器
// 【使用参数传递:便于扩展】 配置上更加语义化
// 将规则作为规则名称封装,【提高了复用性】
// 验证器已经和DOM解耦,只关心规则和数据以及message
var result = validator.test({
// 和id一致才能获取
username: [
{
rule: "isRequired",
value: this.username.value,
message: "用户名不能为空!",
},
],
password1: [
{
rule: "isRequired",
value: this.password1.value,
message: "密码不能为空!",
},
{
rule: "minLength",
value: [this.password1.value, 6],
message: "密码长度不能小于6个字符!",
},
],
password2: [
{
rule: "isRequired",
value: this.password2.value,
message: "确认密码不能为空!",
},
{
rule: "minLength",
value: [this.password2.value, 6],
message: "确认密码长度不能小于6个字符!",
},
{
rule: "isEqual",
value: [this.password2.value, this.password1.value],
message: "确认密码与原密码不相同!",
},
],
isMobile: [
{
rule: "isRequired",
value: this.phone.value,
message: "手机号不能为空!",
},
{
rule: "isMobile",
value: this.phone.value,
message: "手机号格式不正确!",
},
],
});
if (result) {
console.log(result);
} else {
console.log("校验通过");
}
};
</script>
</body>
</html>
策略模式常用于代码的复用性和可扩展性
(7): 适配器模式(Adaptor-Pattern)-结构
这个我们平常很常见
var xiaomi = {
view() {
console.log("显示小米屏幕");
}
}
var iphone = {
view() {
console.log("显示iphone屏幕");
}
}
// 保证手机的高内聚,所以我们吧后续的功能加到外边
// 调用手机,view的功能封装起来
function use(type) {
if(type === 'xiaomi'){
xiaomi.view()
} else if(type === 'iphone') {
iphone.view()
}
}
use("xiaomi")
适配器模式通常用于做代码的兼容