最近公司晉升崗位裏面的一道小題《使用 es3 實現簡單工廠模式、單例模式、觀察者模式、發佈訂閱模式》,結束以後發現本身還要補好多´д` ;,抽空把裏面幾道小題,從新複習了一遍,擴充到es三、es五、es6,並作了筆記(再次膜拜公司出題大佬)。vue
完整版github代碼地址:github.com/xuhuihui/sm…git
定義一個用於建立對象的接口,讓子類決定將哪個類實例化。Factory Method使一個類的實例化延遲到其子類。es6
適用於複雜邏輯判斷的狀況,例如購物的商品,能夠有上架中、出售中、下單中、出貨中、派送中、到手等一系列複雜邏輯判斷。github
關於複雜邏輯的判斷,有兩種解決方案,策略模式和工廠模式。
一、策略模式vs工廠模式的區別
二、策略模式:JavaScript 複雜判斷的更優雅寫法編程
var createPop = function (type, text) {
// 建立一個對象,並對該對象作出擴展
var o = new Object();
o.content = text;
o.show = function () {
// 顯示相同部分
alert(type + ':' + this.content);
switch (type) {
case 'alert':
// 警示框差別部分
break;
case 'confirm':
// 確認框差別部分
break;
case 'prompt':
// 提示框差別部分
break;
}
};
return o;
};
複製代碼
class Image {}
class Link {}
class Text {}
class ElementFactory {
createElement(type, option){
const ELEMENT = {
"image" : Image,
"text" : Text,
"link" : Link
}
let ElementConstructor = ELEMENT(type),
element = null;
if(ElementConstructor){
element = new ElementConstructor(option);
}
return element;
}
}
複製代碼
適用於彈框的實現, 全局緩存。
實現彈框的一種作法是先建立好彈框, 而後使之隱藏, 這樣子的話會浪費部分沒必要要的 DOM 開銷, 咱們能夠在須要彈框的時候再進行建立, 同時結合單例模式實現只有一個實例, 從而節省部分 DOM 開銷。設計模式
一、優勢:利用initialized屬性,保持永遠只建立一個EasySingletonIns實例,
二、缺點:不夠封閉,能夠從外部訪問修改initialized屬性。數組
var EasySingletonIns = {
initialized: false,
// 實例擴展寫在此處:
belongTo: undefined,
getBelongTo: function () {
return EasySingletonIns.belongTo;
}
};
var EasySingleton = {
getInstance: function () {
if (EasySingletonIns.initialized) {
return EasySingletonIns;
}
EasySingletonIns.initialized = true;
// 實例擴展也可寫在此處:
EasySingletonIns.belongTo = 'EasySingleton';
return EasySingletonIns;
}
};
複製代碼
一、優勢:利用函數的閉包,阻止了內部屬性被改變。
二、缺點:很差動態地傳入想要的屬性。瀏覽器
var AdvancedSingleton = (function () {
var ins = null;
return function () {
if (ins) {
return ins;
}
if (!(this instanceof AdvancedSingleton)) {
return new AdvancedSingleton();
}
function Inner() {
// 實例擴展寫在此處:
this.belongTo = 'AdvancedSingleton @ constructor'; }
Inner.prototype = this.constructor.prototype;
// 原型擴展也可寫在此處:
ins = new Inner();
return ins;
};
})();
複製代碼
一、優勢:利用apply,能夠給CoolSingletonCreator實例的prototype增長方法。
二、缺點:構造函數返回非自身實例的狀況下,會出現問題。緩存
function CoolSingletonCreator(fn) {
if (typeof fn !== 'function') {
throw new Error('CoolSingletonCreator fn param must be function!');
}
var AdvancedSingletonForCool = (function () {
var ins = null;
return function () {
if (ins) {
return ins;
}
if (!(this instanceof AdvancedSingletonForCool)) {
return new AdvancedSingletonForCool();
}
var args = arguments;
function Inner() {
fn.apply(this, args);
}
Inner.prototype = this.constructor.prototype;
ins = new Inner();
return ins;
};
})();
AdvancedSingletonForCool.prototype = fn.prototype;
AdvancedSingletonForCool.getInstance = function () {
// 動態參數:(另外,針對只支持es3語法的瀏覽器的bind方法是沒有的,須要本身實現,不在此處展開)
var args = [null];
return new (Function.prototype.bind.apply(AdvancedSingletonForCool, args.concat.apply(args, arguments)))();
};
return AdvancedSingletonForCool;
}
複製代碼
var Singleton = function(name) {
this.name = name;
//一個標記,用來判斷是否已將建立了該類的實例
this.instance = null;
}
// 提供了一個靜態方法,用戶能夠直接在類上調用
Singleton.getInstance = function(name) {
// 沒有實例化的時候建立一個該類的實例
if(!this.instance) {
this.instance = new Singleton(name);
}
// 已經實例化了,返回第一次實例化對象的引用
return this.instance;
}
複製代碼
class Singleton {
constructor(name) {
this.name = name;
this.instance = null;
}
// 構造一個廣爲人知的接口,供用戶對該類進行實例化
static getInstance(name) {
if(!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
}
}
複製代碼
定義對象間的一種一對多的依賴關係,以便當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並自動刷新。bash
一、當觀察的數據對象發生變化時, 自動調用相應函數。好比 vue 的雙向綁定;
二、每當調用對象裏的某個方法時, 就會調用相應'訪問'邏輯。好比給測試框架賦能的 spy 函數;
一、方法:使用prototype綁定函數實現。
var Subject = (function () {
// 觀察者列表(不是必須的,能夠由Subject本身處理)
function ObserverList () {
this.observerList = [];
}
ObserverList.prototype.add = function (observer) {
return this.observerList.push(observer);
};
ObserverList.prototype.remove = function (observer) {
this.observerList = this.observerList.filter(function (item) {return item !== observer;});
};
ObserverList.prototype.count = function () {
return this.observerList.length;
};
ObserverList.prototype.get = function (index) {
return this.observerList[index];
};
// 主題
function Subject () {
this.observers = new ObserverList();
}
Subject.prototype.addObserver = function (observer) {
this.observers.add(observer);
};
Subject.prototype.removeObserver = function (observer) {
this.observers.remove(observer);
};
Subject.prototype.notify = function () {
var observerCount = this.observers.count();
for (let i = 0; i < observerCount; i++) {
var observer = this.observers.get(i);
observer.update.apply(observer, arguments);
}
}
return Subject;
})();
複製代碼
一、方法:使用 Object.defineProperty(obj, props, descriptor) 實現觀察者模式。
二、缺點:Object.defineProperty() 不會監測到數組引用不變的操做(好比 push/pop 等);Object.defineProperty() 只能監測到對象的屬性的改變, 即若是有深度嵌套的對象則須要再次給之綁定 Object.defineProperty();
<input id="input" type="text" />
複製代碼
const data = {}
const input = document.getElementById('input')
Object.defineProperty(data, 'text', {
set(value) {
input.value = value
this.value = value
}
})
input.onchange = function(e) {
data.text = e.target.value
}
複製代碼
一、方法:Proxy/Reflect 是 ES6 引入的新特性, 也可使用其完成觀察者模式。
二、優勢:能夠劫持數組的改變;defineProperty 是對屬性的劫持, Proxy 是對對象的劫持;
var obj = {
value: 0
}
var proxy = new Proxy(obj, {
set: function(target, key, value, receiver) {
Reflect.set(target, key, value, receiver)
}
})
proxy.value = 1 // 調用相應函數
複製代碼
定義:在觀察者模式中間,增長消息代理進行通訊,來實現更更鬆的解耦。
發佈訂閱模式和觀察者模式的差別:
一、在觀察者模式中,觀察者是知道Subject的,Subject一直保持對觀察者進行記錄。然而,在發佈訂閱模式中,發佈者和訂閱者不知道對方的存在。它們只有經過消息代理進行通訊。
二、在發佈訂閱模式中,組件是鬆散耦合的,正好和觀察者模式相反。
三、觀察者模式大多數時候是同步的,好比當事件觸發,Subject就會去調用觀察者的方法。而發佈-訂閱模式大多數時候是異步的(使用消息隊列)。
四、觀察者模式須要在單個應用程序地址空間中實現,而發佈-訂閱更像交叉應用模式。
一、範圍:MVC、MVVC的架構、Vue的源碼實現和一些小遊戲等。
二、優勢: 在異步編程中實現更深的解耦。
三、缺點: 建立訂閱者自己要消耗必定的時間和內存,並且當你訂閱一個消息之後,可能此消息最後都未發生,可是這個訂閱者會始終存在於內存中。若是程序中大量使用發佈-訂閱的話,也會使得程序跟蹤bug變得困難。
var Event = function() {
this.obj = {}
}
Event.prototype.on = function(eventType, fn) {
if (!this.obj[eventType]) {
this.obj[eventType] = []
}
this.obj[eventType].push(fn) // 推入數組
}
Event.prototype.emit = function() {
var eventType = Array.prototype.shift.call(arguments)
var arr = this.obj[eventType]
for (let i = 0; i < arr.length; i++) { //推出調用函數
arr[i].apply(arr[i], arguments)
}
}
var ev = new Event()
ev.on('click', function(a) { // 訂閱函數
console.log(a) // 1
})
ev.emit('click', 1) // 發佈函數
複製代碼
class Subject {
constructor() {
this.subs = []
this.state = '張三' // 觸發更新的狀態
}
getState() {
return this.state
}
setState(state) {
if (this.state === state) {
// 發佈者同樣
return
}
this.state = state
this.notify() // 有更新,觸發通知
}
addSub(sub) {
this.subs.push(sub)
}
removeSub(sub) {
const idx = this.subs.findIndex(i => i === sub)
if (idx === -1) {
// 不存在該觀察者
return
}
this.subs.splice(idx, 1)
}
notify() {
this.subs.forEach(sub => {
sub.update() // 與觀察者原型方法update對應!
})
}
}
// 觀察人,至關於訂閱者
class Observer {
update() {
console.log('update')
}
}
// 測試代碼
const subject = new Subject()
const ob = new Observer()
const ob2 = new Observer()
ob2.update = function() {
//修改update方法,實現不一樣邏輯
console.log('laifeipeng')
}
//目標添加觀察者了
subject.addSub(ob)
subject.addSub(ob2)
//目標發佈消息調用觀察者的更新方法了
// subject.notify(); // 不使用手動觸發,經過內部狀態的設置來觸發
subject.setState('李四')
複製代碼
瞭解到的設計模式以下(゚o゚;;,之後每週有時間大概會補的吧(・_・;。
設計模式分爲三種類型,共24種。