javascript實現23種設計模式
1. 單例模式
全域性唯一例項
var singleton = function( fn ){
var result;
return function(){
return result || ( result = fn .apply( this, arguments ) );//fn.apply相當於直接呼叫函式,只是將函式內的this(上下文)改變了
}
}
var createMask = singleton( function(){
return document.body.appendChild( document.createElement('div' ) );
})
2. 簡單工廠模式
//basketball base class
var Baseketball = function(){
this.intro = 'baseketball is hotting at unitedstates';
}
Baseketball.prototype = {
getMember : function(){\
console.log('each team needs five players');
},
getBallSize : function(){
console.log('basketball is big' );
}
}
//football base class
var Football = function(){
this.intro = 'football is popular at all of the world';
}
Football = function(){
getMember = function(){
},
getBallSize = function(){
}
}
//sport factory
var SportsFactory = function(name){
switch(name){
case 'NBA':
return new Baseketball();
case 'wordCup':
return new Football();
}
}
//when you want football
var football = SportsFactory('wordCup');
console.log(football);
console.log(football.intro);
football.getMember();
三 觀察者模式(釋出者-訂閱者模式 )
面試者把簡歷扔到一個盒子裡, 然後面試官在合適的時機拿著盒子裡的簡歷挨個打電話通知結果.(有任何一個事件發生,所有訂閱者都會通知,只是匹配不上不執行)
Events = function() {
var listen, log, obj, one, remove, trigger, __this;
obj = {};
__this = this;
listen = function( key, eventfn ) { //把簡歷扔盒子, key就是聯絡方式.
var stack, _ref; //stack是盒子
stack = ( _ref = obj[key] ) != null ? _ref : obj[ key ] = [];
return stack.push( eventfn );
};
one = function( key, eventfn ) {
remove( key );
return listen( key, eventfn );
};
remove = function( key ) {
var _ref;
return ( _ref = obj[key] ) != null ? _ref.length = 0 : void 0;
};
trigger = function() { //面試官打電話通知面試者
var fn, stack, _i, _len, _ref, key;
key = Array.prototype.shift.call( arguments );
stack = ( _ref = obj[ key ] ) != null ? _ref : obj[ key ] = [];
for ( _i = 0, _len = stack.length; _i < _len; _i++ ) {
fn = stack[ _i ];
if ( fn.apply( __this, arguments ) === false) {
return false;
}
}
return {
listen: listen,
one: one,
remove: remove,
trigger: trigger
}
}
//訂閱者
var adultTv = Event();
adultTv .listen( ''play', function( data ){
alert ( "今天是誰的電影" + data.name );
});
//釋出者
adultTv .trigger( ''play', { 'name': '麻生希' } )
4. 介面卡模式
介面卡模式的作用很像一個轉介面. 本來iphone的充電器是不能直接插在電腦機箱上的, 而通過一個usb轉介面就可以了.-
$id = function( id ){
return jQuery( '#' + id )[0];
}
5. 代理模式
代理模式的定義是把對一個物件的訪問, 交給另一個代理物件來操作.
舉一個例子, 我在追一個MM想給她送一束花,但是我因為我性格比較靦腆,所以我託付了MM的一個好朋友來送。
6. 橋接模式
橋接模式的作用在於將實現部分和抽象部分分離開來, 以便兩者可以獨立的變化。
7. 外觀模式
外觀模式提供一個高層介面,這個介面使得客戶端或子系統更加方便呼叫。簡單講,就是將小的api封裝成大的api
var stopEvent = function( e ){ //同時阻止事件預設行為和冒泡
e.stopPropagation();
e.preventDefault();
}
8. 訪問者模式
訪問者模式先把一些可複用的行為抽象到一個函式(物件)裡,這個函式我們就稱為訪問者(Visitor)。如果另外一些物件要呼叫這個函式,只需要把那些物件當作引數傳給這個函式,在js裡我們經常通過call或者apply的方式傳遞this物件給一個Visitor函式.
var Visitor = {}
Visitor .push = function(){
return Array.prototype.push.apply( this, arguments );
}
var obj = {};
obj.push = Visitor .push;
obj.push( '"first" );
alert ( obj[0] ) //"first"
alert ( obj.length ); //1
9.策略模式
策略模式的意義是定義一系列的演算法,把它們一個個封裝起來,並且使它們可相互替換。
一個小例子就能讓我們一目瞭然。
$( div ).animate( {"left: 200px"}, 1000, 'linear' ); //勻速運動
$( div ).animate( {"left: 200px"}, 1000, 'cubic' ); //三次方的緩動
10. 模版方法模式
模式方法是預先定義一組演算法,先把演算法的不變部分抽象到父類,再將另外一些可變的步驟延遲到子類去實現。實現思路就是繼承,覆蓋父類方法
Mammal.prototope.出生 = function(){
'胎生()
}
Mammal.prototype.成長 = function(){
//再留給子類去實現
}
Mammal.prototope.衰老 = function(){
自由基的過氧化反應()
}
Life.prototype.死亡 = function(){
//再留給子類去實現
}
//再實現一個Dog類
var = Dog = function(){
}
//Dog繼承自哺乳動物.
Dog.prototype = Mammal.prototype;
var dog = new Dog();
dog.init();
12. 迭代器模式
迭代器模式提供一種方法順序訪問一個聚合物件中各個元素,而又不需要暴露該方法中的內部表示。
js中我們經常會封裝一個each函式用來實現迭代器。
array的迭代器:
forEach = function( ary, fn ){ for ( var i = 0, l = ary.length; i < l; i++ ){ var c = ary[ i ]; if ( fn.call( c, i , c ) === false ){ return false; } }}
forEach( [ 1, 2, 3 ], function( i, n ){
alert ( i );
})
13.組合模式
又叫部分-整體模式,它將所有物件組合成樹形結構。使得使用者只需要操作最上層的介面,就可以對所有成員做相同的操作。
form.validata函式, 它負責把真正的validata操作分發給每個組合物件.
form.validata函式裡面會依次遍歷所有需要校驗的field. 若有一個field校驗未通過, form.validata都會返回false. 虛擬碼如下.
form.validata = function(){
forEach( fields, function( index, field ){
if ( field.validata() === false ){
return false;
}
})
return true;
}
14. 備忘錄模式
備忘錄模式在js中經常用於資料快取. 比如一個分頁控制元件, 從伺服器獲得某一頁的資料後可以存入快取。以後再翻回這一頁的時候,可以直接使用快取裡的資料而無需再次請求伺服器。
實現比較簡單,虛擬碼:
var Page = function(){
var page = 1,
cache = {},
data;
return function( page ){
if ( cache[ page ] ){
data = cache[ page ];
render( data );
}else{
Ajax.send( 'cgi.xx.com/xxx', function( data ){
cache[ page ] = data;
render( data );
})
}
}
}()
15. 職責鏈模式
js中的事件冒泡就是作為一個職責鏈來實現的。一個事件在某個節點上被觸發,然後向根節點傳遞, 直到被節點捕獲。
16. 享元模式
享元模式可以提供一些共享的物件以便重複利用. 仔細看下上圖, 其實我們一共只需要10個div來顯示好友資訊,也就是出現在使用者視線中的10個div.這10個div就可以寫成享元.
var getDiv = (function(){
var created = [];
var create = function(){
return document.body.appendChild( document.createElement( 'div' ) );
}
var get = function(){
if ( created.length ){
return created.shift();
}else{
return create();
}
}
/* 一個假設的事件,用來監聽剛消失在視線外的div,實際上可以通過監聽滾 動條位置來實現 */
userInfoContainer.disappear(function( div ){
created.push( div );
})
})()
var div = getDiv();
div.innerHTML = "${userinfo}";
17. 狀態模式
通過這個狀態類,可以把散落在世界各地的條件分支集中管理到一個類裡,並且可以很容易的新增一種新的狀態。而作為呼叫者,只需要通過暴露的changeState介面來切換人物的狀態。
var StateManager = function(){
var currState = 'wait';
var states = {
jump: function( state ){
},
wait: function( state ){
},
attack: function( state ){
},
crouch: function( state ){
},
defense: function( state ){
if ( currState === 'jump' ){
return false; //不成功,跳躍的時候不能防禦
}
//do something; //防禦的真正邏輯程式碼, 為了防止狀態類的程式碼過多, 應該把這些邏輯繼續扔給真正的fight類來執行.
currState = 'defense'; // 切換狀態
}
}
var changeState = function( state ){
states[ state ] && states[ state ]();
}
return {
changeState : changeState
}
}
var stateManager = StateManager();
stateManager.changeState( 'defense' );