JS設(shè)計(jì)模式有很多種
在這里插入圖片描述
常見的設(shè)計(jì)模式
(1): 單例模式(Singleton Pattern)-創(chuàng)建
1: 第一種
// 單例模式(Singleton Pattern),后端用的比較多
// 單例模式創(chuàng)建多少對象就只有一個實(shí)例
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);
在這里插入圖片描述
應(yīng)用場景:數(shù)據(jù)一致性
(2): 工廠模式(Factory Pattern)- 創(chuàng)建
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: '學(xué)生',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 };
// 數(shù)學(xué)
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 = '數(shù)學(xué)';
break;
}
return new Clazz(data)
}
var c1 = FactoryPattern("history")
var c2 = FactoryPattern("math")
console.log(c1,c2);
在這里插入圖片描述
工廠模式應(yīng)用場景:工廠模式-創(chuàng)建(批量生產(chǎn)對象)
(3): 觀察者模式(Observer Pattern)-行為
1: ES5寫法
// 依賴收集器
function Dep() {
this.subs = [] // 數(shù)據(jù)存儲,存儲需要訂閱的內(nèi)容,未來方便通知
}
Dep.prototype.addSub = function(sub) {
this.subs.push(sub)
}
// 事件觸發(fā)方:通知的行為
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")
// 創(chuàng)建主題對象,對主題產(chǎn)生訂閱
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 負(fù)責(zé)視圖更新,Dep負(fù)責(zé)收集依賴和通知,視圖和邏輯分離,具備消息訂閱功能
應(yīng)用場景: 消息訂閱
附加:手寫一個eventsBus
class Bus {
constructor(){
this.handlers = {}
}
// 實(shí)現(xiàn)事件訂閱on
on(name,callback) {
// 沒有就新增
if(!this.handlers[name]) {
this.handlers[name] = []
}
// 有的話就進(jìn)行push
this.handlers[name].push(callback)
}
// 實(shí)現(xiàn)事件發(fā)布emit
emit(name,...args) {
if(this.handlers[name]) {
this.handlers[name].forEach(cb=>{
cb(...args)
})
}
}
// 實(shí)現(xiàn)事件的銷毀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)-結(jié)構(gòu)
// Decorator Pattern 裝飾器模式
function Coffee() {
this.info = "苦咖啡";
}
Coffee.prototype.show = function() {
console.log(this.info); // 這個this就是構(gòu)造函數(shù)本身
}
// 裝飾器
function CoffeeDecorator(coffee) {
this.coffee = coffee; // 保存被裝飾的對象
}
// 加工外部操作,更加利于加工類的擴(kuò)展和維護(hù)
CoffeeDecorator.prototype.show = function() {
console.log("糖是甜的呦!");
this.coffee.show()
}
var co1 = new Coffee();
var coDc = new CoffeeDecorator(co1)
coDc.show()
在這里插入圖片描述
(5) 代理模式(Proxy-Pattern)
// 創(chuàng)建代理類
function Coffee() {
this.info = "苦咖啡"; // 代碼不利于維護(hù)
}
Coffee.prototype.show = function() {
console.log(this.info);
}
// 創(chuàng)建代理類
function CoffeeProxy() {
// 用代理替換文本 不存在本體
this.coffee = new Coffee()
}
CoffeeProxy.prototype.show = function() {
console.log("代理的甜");
this.coffee.show()
}
var cp1 = new CoffeeProxy()
cp1.show()
其實(shí)裝飾器模式和代理模式很相似,如何選擇呢,根據(jù)當(dāng)前是否有裝飾著實(shí)例,如果沒有,就用代理模式創(chuàng)建一個實(shí)例
(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="確認(rèn)密碼" /><br />
<input type="text" id="phone" placeholder="電話" /><br />
<button>提交</button>
</form>
<script>
function Validate() {}
// 四個功能函數(shù), 通過外部 調(diào)用rules[名稱]獲取
Validate.prototype.rules = {
// 是否手機(jī)號
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 () {
// 可以接收多個參數(shù)比較
var args = Array.prototype.slice.call(arguments);
// 取首項(xiàng)與后面所有的項(xiàng)比較,如果每個都相等,就返回true
var equal = args.every(function (value) {
return value === args[0];
});
return equal;
},
};
Validate.prototype.test = function (rules) {
var v = this;
var valid; // 保存校驗(yàn)結(jié)果
// 外部傳遞的規(guī)則配置參數(shù)rules
for (var key in rules) {
// 遍歷校驗(yàn)規(guī)則對象
// 數(shù)組
for (var i = 0; i < rules[key].length; i++) {
// 遍歷每一個字段的校驗(yàn)規(guī)則
var ruleName = rules[key][i].rule; // 獲取每一個校驗(yàn)規(guī)則的規(guī)則名
var value = rules[key][i].value; // 獲取每一個校驗(yàn)規(guī)則的校驗(yàn)值
if (!Array.isArray(value)) {
// 統(tǒng)一校驗(yàn)值為數(shù)組類型
value = new Array(value);
}
// ruleName // 確保this是validate對象
// [this.password2.value, 6] || this.password2.value
var result = v.rules[ruleName].apply(v, value); // 調(diào)用校驗(yàn)規(guī)則方法進(jìn)行校驗(yàn)
if (!result) {
// 如果校驗(yàn)不通過,就獲取校驗(yàn)結(jié)果信息,并立即跳出循環(huán)不再執(zhí)行,節(jié)約消耗
valid = {
errValue: key,
errMsg: rules[key][i].message,
};
break;
}
}
if (valid) {
// 如果有了校驗(yàn)結(jié)果,代表存在不通過的字段,則立即停止循環(huán),節(jié)約消耗
break;
}
}
return valid; // 把校驗(yàn)結(jié)果反悔出去
};
var formData = document.getElementById("form");
formData.onsubmit = function () {
event.preventDefault();
var validator = new Validate();
// 驗(yàn)證規(guī)則 抽離出來, 便于更為靈活的應(yīng)用驗(yàn)證器
// 【使用參數(shù)傳遞:便于擴(kuò)展】 配置上更加語義化
// 將規(guī)則作為規(guī)則名稱封裝,【提高了復(fù)用性】
// 驗(yàn)證器已經(jīng)和DOM解耦,只關(guān)心規(guī)則和數(shù)據(jù)以及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: "確認(rèn)密碼不能為空!",
},
{
rule: "minLength",
value: [this.password2.value, 6],
message: "確認(rèn)密碼長度不能小于6個字符!",
},
{
rule: "isEqual",
value: [this.password2.value, this.password1.value],
message: "確認(rèn)密碼與原密碼不相同!",
},
],
isMobile: [
{
rule: "isRequired",
value: this.phone.value,
message: "手機(jī)號不能為空!",
},
{
rule: "isMobile",
value: this.phone.value,
message: "手機(jī)號格式不正確!",
},
],
});
if (result) {
console.log(result);
} else {
console.log("校驗(yàn)通過");
}
};
</script>
</body>
</html>
策略模式常用于代碼的復(fù)用性和可擴(kuò)展性
在這里插入圖片描述
(7): 適配器模式(Adaptor-Pattern)-結(jié)構(gòu)
這個我們平常很常見
var xiaomi = {
view() {
console.log("顯示小米屏幕");
}
}
var iphone = {
view() {
console.log("顯示iphone屏幕");
}
}
// 保證手機(jī)的高內(nèi)聚,所以我們吧后續(xù)的功能加到外邊
// 調(diào)用手機(jī),view的功能封裝起來
function use(type) {
if(type === 'xiaomi'){
xiaomi.view()
} else if(type === 'iphone') {
iphone.view()
}
}
use("xiaomi")
適配器模式通常用于做代碼的兼容