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

推荐订阅源

L
LangChain Blog
V
V2EX
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
小众软件
小众软件
Vercel News
Vercel News
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
J
Java Code Geeks
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog
B
Blog
美团技术团队
量子位

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