惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

腾讯CDC
The Cloudflare Blog
IT之家
IT之家
V
V2EX
雷峰网
雷峰网
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
Stack Overflow Blog
Stack Overflow Blog
博客园 - Franky
Engineering at Meta
Engineering at Meta
S
SegmentFault 最新的问题
GbyAI
GbyAI
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
小众软件
小众软件
博客园 - 叶小钗
Blog — PlanetScale
Blog — PlanetScale
C
Check Point Blog
A
About on SuperTechFans
B
Blog
月光博客
月光博客
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI

博客园 - Truly

找不到具有绑定 MetadataExchangeHttpBinding 的终结点的与方案 http 匹配的基址。注册的基址方案是 [https] 生成8位的不重复乱码 省地市级联选择js 继续完善SandCastle 使用SandCastle和ScriptDoc创建JavaScript文档 关于.NET的异常处理的几个误区 JavaScript实现自定义事件 JavaScript面向对象之属性实现 JavaScript随笔一篇 - Truly - 博客园 关于JavaScript的“+”运算和“-”运算和三目操作符“?:” - Truly - 博客园 微软ASP.NET AJAX中日期类型的JSON处理 JavaScript面向对象之方法重载 谁动了我的奶酪? 设计模式在JavaScript中的应用(1) 在JavaScript中使用面向对象 对于Asp.Net 2.0中脚本资源的研究(2) 对于Asp.Net 2.0中脚本资源的研究(1) js功能类库放送(二) 调整大小库 js功能类库放送(一) 拖动功能库
设计模式在JavaScript中的应用(2) -- Observer
Truly · 2007-07-31 · via 博客园 - Truly

作者: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框架,不过根据行文需要,我做了适当改动 :)