導(dǎo)讀:
日常開發(fā)中,一些特定的場景下你的處理方法可能并不是很理想,往往這時(shí)借助一些設(shè)計(jì)模式可以讓你優(yōu)雅而高效的實(shí)現(xiàn)這些邏輯,下面就介紹一些雖然不是最全的但一定是最常用的設(shè)計(jì)模式。
單例模式:
定義:一個(gè)類只返回一個(gè)實(shí)例,一旦創(chuàng)建再次調(diào)用就直接返回
使用場景:比如自定義彈窗,無論你程序中多少調(diào)用,都只應(yīng)創(chuàng)建一個(gè)彈窗對(duì)象
class CreateUser {
constructor(name) {
this.name = name;
this.getName();
}
getName() {
return this.name;
}
};
const ProxyMode = (() => {
let instance = null;
return (name) => {
if(!instance) {
instance = new CreateUser(name);
}
return instance;
}
})();
let a = ProxyMode('vn');
let b = ProxyMode('lb');
console.log(a, b); // vn vn 單例模式只會(huì)創(chuàng)建一次實(shí)例
策略模式:
定義:定義一個(gè)策略類只專注與各方法算法實(shí)現(xiàn),定義一個(gè)接口調(diào)用這些方法
特點(diǎn):代碼優(yōu)雅,可讀性高
// 策略類
const levelObj = {
"A": money => money * 4,
"B": money => money * 3,
"C": money => money * 2
}
// 環(huán)境類 封裝調(diào)用接口
const getMoney = (level, money) => levelObj[level](money);
console.log(getMoney('A', 200)) // 800
代理模式:
定義:為一個(gè)對(duì)象提供一個(gè)代用品或占位符,以便控制對(duì)它的訪問
使用場景:比如圖片懶加載,先緩存動(dòng)態(tài) loading,必要時(shí)傳入 src
const imgFunc = (() => {
let imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: (src) => {
imgNode.src = src;
}
}
})();
const ProxyImg = (() => {
let img = new Image();
img.onload = () => {
let node = document.getElementsByTagName('img')
imgFunc.setSrc(img.src);
}
return {
setSrc: (src) => {
imgFunc.setSrc('../C3photo/jacky/1.jpg');
img.src = src;
}
}
})();
ProxyImg.setSrc('../C3photo/jacky/2.jpg');
裝飾者模式:
定義:裝飾者模式能夠在不改變對(duì)象自身的基礎(chǔ)上,在運(yùn)行程序期間給對(duì)象動(dòng)態(tài)地添加職責(zé)。
使用場景:類似于攔截器,添加對(duì)象的前置和后置事件等。
Function.prototype.before = function(beforefn) {
let _self = this; //保存原函數(shù)引用
return function(){ //返回包含了原函數(shù)和新函數(shù)的 '代理函數(shù)'
beforefn.apply(this, arguments); //執(zhí)行新函數(shù),修正this
return _self.apply(this, arguments); //執(zhí)行原函數(shù)
}
}
Function.prototype.after = function(afterfn) {
let _self = this;
return function(){
let ret = _self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
}
}
let func = function() {
console.log('2');
}
//func1和func3為掛載函數(shù)
let func1 = function() {
console.log('1');
}
let func3 = function() {
console.log('3');
}
func = func.before(func1).after(func3);
func(); // 1 2 3
發(fā)布訂閱模式:
定義:訂閱者(Subscriber)把自己想訂閱的事件注冊(Subscribe)到調(diào)度中心(Event Channel),當(dāng)發(fā)布者(Publisher)發(fā)布該事件(Publish Event)到調(diào)度中心,也就是該事件觸發(fā)時(shí),由調(diào)度中心統(tǒng)一調(diào)度(Fire Event)訂閱者注冊到調(diào)度中心的處理代碼。
使用場景:微信公眾號(hào)的訂閱
let eventEmitter = {
list: {}, // 緩存列表(調(diào)度中心)
on(event, fn) { // 訂閱
let _this = this;
_this.list[event] = _this.list[event] || [];
_this.list[event].push(fn);
return _this;
},
emit() { // 發(fā)布
let _this = this;
let event = [].shift.call(arguments), // shift 會(huì)改變原數(shù)組,因此 arguments 只剩下第二個(gè)參數(shù)
fns = _this.list[event];
if(fns && fns.length) {
fns.forEach(fn => fn.apply(_this, arguments));
}
return _this;
},
off(event, fn) { // 取消訂閱
let _this = this;
let fns = _this.list[event];
if(!fns) return false; // 如果緩存列表中沒有相應(yīng)的 fn,返回false
if(!fn) {
// 如果沒有傳 fn 的話,就會(huì)將 event 值對(duì)應(yīng)緩存列表中的 fn 都清空
fns.length = 0;
} else {
// 若有 fn,遍歷緩存列表,看看傳入的 fn 與哪個(gè)函數(shù)相同,如果相同就直接從緩存列表中刪掉即可
for (let i = 0; i < fns.length; i++) {
if (fns[i] === fn || fns[i].fn === fn) {
fns.splice(i, 1);
break;
}
}
}
}
};
const user1 = (content) => {
console.log('用戶1訂閱了:', content);
}
const user2 = (content) => {
console.log('用戶2訂閱了:', content);
}
const user3 = (content) => {
console.log('用戶3訂閱了:', content);
}
// 訂閱
eventEmitter.on('article1', user1);
eventEmitter.on('article1', user2);
eventEmitter.on('article2', user3);
eventEmitter.emit('article1', 'Javascript 發(fā)布-訂閱模式');
eventEmitter.emit('article2', 'Javascript 觀察者模式');
eventEmitter.off('article1', user1);
eventEmitter.emit('article1', 'Javascript 發(fā)布-訂閱模式');
//用戶1訂閱了: Javascript 發(fā)布-訂閱模式
//用戶2訂閱了: Javascript 發(fā)布-訂閱模式
//用戶3訂閱了: Javascript 觀察者模式
//用戶2訂閱了: Javascript 發(fā)布-訂閱模式
總結(jié):
其實(shí)當(dāng)你學(xué)了這么久的前端,做了這么久的項(xiàng)目,回來看看設(shè)計(jì)模式會(huì)發(fā)現(xiàn)它的思想其實(shí)挺有意思的,當(dāng)你試著在工作中寫出這些設(shè)計(jì)模式,你的技術(shù)又會(huì)上一層臺(tái)階啦。