1. 程式人生 > >JavaScript設計模式(代理模式)

JavaScript設計模式(代理模式)

一、簡單的單例模式:

1、未使用代理模式的情況:小明直接給女神送花

var Flower = function() {}
var xiaoming = {
  sendFlower: function( target ){
    var flower = new Flower();
    target.receiveFlower( flower ); 
  }
};
var A = {
  receiveFlower: function( flower ){
    console.log( '收到花 ' , flower ); 
  }
};
xiaoming.sendFlower( A );

2、使用簡單的代理模式:小明

var Flower = function() {}
var xiaoming = {
  sendFlower: function( target ){
    var flower = new Flower();
    target.receiveFlower( flower ); 
  }
};
var A = {
  receiveFlower: function( flower ){
    console.log( '收到花 ' , flower ); 
  }
};
var B = {
    receiveFlower: function( flower ){
      A.receiveFlower(flower)
    }
}
xiaoming.sendFlower( B );

二、保護代理和虛擬代理

雖然這只是個虛擬的例子,但我們可以從中找到兩種代理模式的身影。代理 B 可以幫助 A 過濾掉一些請求,比如送花的人中年齡太大的或者沒有寶馬的,這種請求就可以直接在代理 B 處被拒絕掉。這種代理叫作保護代理。A 和 B 一個充當白臉,一個充當黑臉。白臉 A 繼續保持 良好的女神形象,不希望直接拒絕任何人,於是找了黑臉 B 來控制對 A 的訪問。

另外,假設現實中的花價格不菲,導致在程式世界裡,new Flower 也是一個代價昂貴的操作, 那麼我們可以把 new Flower 的操作交給代理 B 去執行,代理 B 會選擇在 A 心情好時再執行 new Flower,這是代理模式的另一種形式,叫作虛擬代理。虛擬代理把一些開銷很大的物件,延遲到 真正需要它的時候才去建立。

三、虛擬代理實現圖片預載入

var myImage = (function(){
  var imgNode = document.createElement( 'img' ); 
  document.body.appendChild( imgNode );
  return {
    setSrc: function( src ){
      imgNode.src = src; 
    }
  } 
})();
myImage.setSrc('http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg');
普通方式下,我們把網速調至 5KB/s,然後通過 MyImage.setSrc 給該 img 節點設定 src,可以看到,在圖片被載入好之前,頁面中有一段長長的空白時間。
var myImage = (function(){
  var imgNode = document.createElement( 'img' ); 
  document.body.appendChild( imgNode );
  return {
    setSrc: function( src ){
      imgNode.src = src; 
    }
  } 
})();
var proxyImage = (function(){
  var img = new Image; 
  img.onload = function(){
    myImage.setSrc(this.src); 
  }
  return {
    setSrc: function( src ){
      myImage.setSrc( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );
      img.src = src; 
    }
  } 
})();
proxyImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );
代理模式改造後,現在開始引入代理物件 proxyImage,通過這個代理物件,在圖片被真正載入好之前,頁面中將出現一張佔位的菊花圖 loading.gif, 來提示使用者圖片正在載入。proxyImage 控制了客戶對 MyImage 的訪問,並且在此過程中加入一些額外的操作,比如在真正的圖片載入好之前,先把 img 節點的 src 設定為 一張本地的 loading 圖片。 四、虛擬代理合並HTTP請求
<body>
  <input type="checkbox" id="1"></input>1
  <input type="checkbox" id="2"></input>2
  <input type="checkbox" id="3"></input>3
  <input type="checkbox" id="4"></input>4
  <input type="checkbox" id="5"></input>5
  <input type="checkbox" id="6"></input>6
  <input type="checkbox" id="7"></input>7
  <input type="checkbox" id="8"></input>8
  <input type="checkbox" id="9"></input>9
</body>
給這些 checkbox 繫結點選事件,並且在點選的同時往另一臺伺服器同步檔案:當我們選中3個checkbox 的時候,依次往伺服器傳送了3次同步檔案的請求。而點選一個checkbox並不是很複雜的操作。可以預見,如此頻繁的網路請求將會帶來相當大的開銷。
var synchronousFile = function( id ){ 
  console.log( '開始同步檔案,id 為: ' + id );
};
var checkbox = document.getElementsByTagName( 'input' );
for ( var i = 0, c; c = checkbox[ i++ ]; ){ 
  c.onclick = function(){
    if ( this.checked === true ){ 
      synchronousFile( this.id );
    } 
  }
};

解決方案是,我們可以通過一個代理函式 proxySynchronousFile 來收集一段時間之內的請求, 最後一次性發送給伺服器。比如我們等待 2 秒之後才把這 2 秒之內需要同步的檔案 ID 打包發給 伺服器,如果不是對實時性要求非常高的系統,2 秒的延遲不會帶來太大副作用,卻能大大減輕 伺服器的壓力。:

var synchronousFile = function( id ){ 
  console.log( '開始同步檔案,id 為: ' + id );
};
var proxySynchronousFile = (function(){
  var cache = [], // 儲存一段時間內需要同步的 ID
  timer; // 定時器
  return function( id ){
    cache.push( id );
    if ( timer ){ // 保證不會覆蓋已經啟動的定時器
      return; 
    }
    timer = setTimeout(function(){ 
      synchronousFile( cache.join( ',' ) ); 
      clearTimeout( timer ); // 清空定時器 timer = null;
      cache.length = 0; // 清空 ID 集合
    }, 2000); 
  }
  // 2 秒後向本體傳送需要同步的 ID 集合
})();
var checkbox = document.getElementsByTagName( 'input' ); 
for ( var i = 0, c; c = checkbox[ i++ ]; ){
  c.onclick = function(){
    if ( this.checked === true ){
      proxySynchronousFile( this.id ); 
    }
  }
}