設計模式在JavaScript中的應用(2) — Observer

來源:互聯網
上載者:User
作者:Truly
日期:2007.7.31

上次我們討論了Web開發中最重要的設計模式MVC,今天我們要討論的是Observer模式,與MVC這樣的大型設計模式相比,Observer模式則要輕量很多。廢話不多說了,進入主題

Obsever簡單應用

請先看一段代碼:

// the process array calling after page loaded for page listener.
var PageLoadListener = new Array(); 

// page listener
function onDocumentLoaded()
{
    for (var a in PageLoadListener)
    {
        if(typeof  PageLoadListener[a]  == 'function')
            PageLoadListener[a]();
    }
}
// Add a listener to current page to run all function on the page.
if (document.addEventListener)
 document.addEventListener('DOMContentLoaded', onDocumentLoaded, false);
else  
 window.attachEvent('onload', onDocumentLoaded); 

而在另外一個js中我們定義:

PageLoadListener.push(domLoaded);    // push the domLoaded function into the listener array.

// a method need to call after page is loaded
function domLoaded()
{
    alert('document loaded');
}

通常我們經常要處理window.onload事件,例如使用下面代碼來指定onload事件

window.onload=aFunction

    而當我們這樣的方式聲明的時候,很可能會覆蓋已經定義過的window.onload事件,或者我們這裡還有很多事件要在onload執行,那麼如何應對這種情況呢?Observer模式恰好可以用來處理這種情況。首先我們需要為頁面定義了一個監聽器,檢測頁面中需要處理的事件,然後定義一個全域的監聽器數組。這樣需要處理的事件都可以註冊到這個監聽器數組中,然後統一進行調用。
   
    如上面代碼中的,我們將需要處理的事件名通過下面代碼

PageLoadListener.push(domLoaded); 

    註冊到Listener數組中,這樣的註冊過程可能遍布到不同的js檔案或指令碼塊中,最後使用監聽器集中對數組中的元素進行調用,這樣以來就很好的解決了window.onload事件衝突的問題。

Obsever進階應用

下面我們示範一個更加複雜的Obsever模式應用,來自著名的Prototype架構,請先查看代碼:

Hello.htm

<html>
<head>
<title>Obsever Demo</title>
<script language="javascript" type="text/javascript" src="Obsever.js"></script>
<script language="javascript" type="text/javascript" src="Controller.js"></script>
</head>
<body>
    <input id='textbox1' name='textbox1'/>
    <select id='selElement1' >
        <option >choose</option>
        <option value='1' >1</option>
        <option value='2'>2</option>
        <option value='3'>3</option>
    </select>
</body>
</html>


Obsever.js


function $(id){return document.getElementById(id);}
var $A = Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
    return iterable.toArray();
  } else {
    var results = [];
    for (var i = 0, length = iterable.length; i < length; i++)
      results.push(iterable[i]);
    return results;
  }
}
var Browser={
    isWebKit : navigator.userAgent.indexOf('AppleWebKit/') > -1
    }
Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}
if (!window.Event) {
  var Event = new Object();
}
Object.extend = function(destination, source) {
  for (var property in source) {
    destination[property] = source[property];
  }
  return destination;
}
Object.extend(Event,
{
observe: function(element, name, observer, useCapture) {
    if(typeof element != 'object')
    element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
      (isWebKit || element.attachEvent))
      name = 'keydown';

    Event._observeAndCache(element, name, observer, useCapture);
  },

stopObserving : function(element, name, observer, useCapture) {
    element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (Browser.WebKit || element.attachEvent))
      name = 'keydown';

    if (element.removeEventListener) {
      element.removeEventListener(name, observer, useCapture);
    } else if (element.detachEvent) {
      try {
        element.detachEvent('on' + name, observer);
      } catch (e) {}
    }
  },
  observers: false,
  _observeAndCache: function(element, name, observer, useCapture) {
    if (!this.observers) this.observers = [];
    if (element.addEventListener) {
      this.observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } else if (element.attachEvent) {
      this.observers.push([element, name, observer, useCapture]);
      element.attachEvent('on' + name, observer);
    }
  }
}
)


Controller.js

function changeHandler()
{
    $('textbox1').value=this.value;
}
Event.observe(window, 'load',
    function(){Event.observe('selElement1','change',changeHandler.bind($('selElement1')));}
);

上面代碼示範了一個當選擇下拉框的時候,調整文字框的值。我們示範了onchange和onload事件的監聽,同樣的也可以應用到任何DOM節點的各個事件上。但是,你可能說你可以在<select>標籤中直接添加onchange事件就可以了,為什麼要這麼做?

Well,首先這樣可以更好的分離代碼和視圖,就像我上篇文章中討論的MVC模式,我們應該儘可能的分離代碼和視圖。尤其是當你構建一個大型的應用程式的時候,例如飛鴿這樣的網站,越是可以從中受益。
    
同時通過這種方式,可以設計出一個完整的用戶端事件流程。關於JavaScript事件模型的討論,將是我們後面文章的討論內容。

註:文中代碼部分取自著名的Prototype架構,不過根據行文需要,我做了適當改動 :)
 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.