javascript实现事件监控

在开发中有时候需要实现一个对象A的变化然后更新另外一个对象B

这个实现的最简单的方式时在目标对象A的方法中添加B更新的逻辑代码

但是我们希望能够用一种比较优雅的方式实现,比如当需求变化时不需要改A的代码,并且可以随时添加或者删除处理函数。

在大多数gui编程中,都会提供这个事件机制。

在网页的页面交互中,我们可以注册自己的方法到输入框的clikc或者change事件中,

document.getElementByTag("input").addEventListener("click",fn);

document.getElementByTag("input").removeEventListener("click",fn);

//IE中方法使用attachEvent

这样就能把fn处理方法绑定或者解绑到input的click事件中

不过,通过这种方式绑定的事件event只能是对象自带的

比如,只能注册 “click”,"focus","blur","change","load","mouseout"等一些对象元素自带的事件

如果我们想给input添加一个自定义事件或者给自定义的panel添加事件处理程序的话,只能通过自己实现了。

下面来个实现

 1             //输入框变化,改变其他对象
 2             //事件插件,其他对象可扩展此对象进行事件监控
 3             var Events = (function () {
 4                 var events = {};
 5                 return {
 6                     addListeners: function (fn, callback) {
 7                         events[fn] = events[fn] || [];
 8                         events[fn].push(callback);
 9                     },
10                     removeListeners: function (fn, callback) {
11                         events[fn] = events[fn].filter(item => {
12                             return item != callback;
13                         })
14                     },
15                     fireEvent: function (fn, param) {
16                         var args = Array.prototype.slice.call(arguments, 0),
17                             me = this;
18                         if (events[fn]) {
19                             events[fn].forEach(item => {
20                                 item.call(this, param);
21                             })
22                         }
23                     }
24                 }
25             })();
26
27             //输入组件,和他的两个实例方法
28             function Input() { }
29             Input.prototype.userInput = function (param) {
30                 this.fireEvent("change", param);
31             }
32             Input.prototype.userLeave = function (param) {
33                 this.fireEvent("keyup", param);
34             }
35
36             //监控输入组件,根据输入框变化更新此面板
37             function Panel() { }
38             Panel.prototype.showClick = function (param) {
39                 console.log("Panel show");
40             }
41             Panel.prototype.hideClick = function (param) {
42                 console.log("Panel hide");
43             }
44
45         //实例化input对象,并继承Events的属性和方法
46             var input = new Input();
47             Object.assign(input, Events);
48
49             var panel = new Panel();
50
51         //注册处理程序到change和keyup事件
52             input.addListeners("change", panel.showClick);
53             input.addListeners("keyup", panel.hideClick);
54
55             //通过实例方法触发change和keyup事件
56             input.userInput();  //输出结果:--Panel show
57             input.userLeave();  //输出结果:--Panel hide

上面Events对象 是个简易的事件绑定方法,所有其他的对象都可以扩展此方法来实现事件的绑定。

C#用委托来实现事件绑定

 1 using System;
 2
 3 namespace ConsoleApp2
 4 {
 5
 6     public class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             var subject = new Subject();
11             var observer = new Observer(subject);
12             subject.update();
13             Console.ReadLine();
14
15         }
16     }
17
18     public delegate void ChangeHandler();
19
20     public class Subject
21     {
22
23         public ChangeHandler changeHandler;
24         public void update()
25         {
26             //if (changeHandler != null)
27             //{
28             //    changeHandler();
29             //}
30             changeHandler?.Invoke();
31
32         }
33     }
34
35     public class Observer
36     {
37         public Observer(Subject subject)
38         {
39             subject.changeHandler += new ChangeHandler(change);
40         }
41         public void change()
42         {
43             Console.WriteLine("subject had updated");
44         }
45     }
46 }

通过一个对象改变从而引起其他对象变化的情况,在gof书籍中称为观察者模式,或者叫发布-订阅模式  ,属于对象行为型模式

意图

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都得到通知并被自动更新

适用性

适用于以下的任一种情况

  • 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这二者封装在独立的对象中以使它们可以各自独立的改变和复用
  • 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变
  • 当一个对象必须通知其他对象,而他又不能假定其他对象是谁。换言之,你不希望这些对象是紧密耦合的。

下面来实现这个模型

我们把这个状态变化的对象成为目标对象(Subject),根据目标对象变化而变化的对象成为观察者(Observers)

目标对象

 1 //目标对象基类
 2 public abstract class BasSubject
 3     {
 4         //观察者对象
 5         public List<BasObserver> observerList = new List<BasObserver>();
 6         //状态
 7         public string State
 8         {
 9             get; set;
10         }
11         /// <summary>
12         /// 附加观察者
13         /// </summary>
14         /// <param name="observer"></param>
15         public void attachObserver(BasObserver observer)
16         {
17             observerList.Add(observer);
18         }
19         /// <summary>
20         /// 移除观察者
21         /// </summary>
22         /// <param name="observer"></param>
23         public void detachObserver(BasObserver observer)
24         {
25             observerList.Remove(observer);
26         }
27         /// <summary>
28         /// 状态变更
29         /// </summary>
30         public virtual  void onChangeState()
31         {
32             observerList.ForEach(async item =>
33             {
34                 await item.update();
35             });
36
37         }
38
39     }
40 // 子类
41    public class Subject:BasSubject
42     {
43         public void onChangeState(string state)
44         {
45             State = state;
46             base.onChangeState();
47         }
48     }

观察者对象 

 1 //观察者抽象类或者接口
 2   public abstract class BasObserver
 3     {
 4         public BasSubject subject;
 5         public BasObserver() {
 6
 7         }
 8         public BasObserver(BasSubject subject) {
 9             this.subject = subject;
10             this.subject.attachObserver(this);
11         }
12         public virtual async Task update() {
13         }
14     }
15 //对象A
16     public class ObserverA : BasObserver
17     {
18         BasSubject subject;
19         public ObserverA()
20         {
21
22         }
23         public ObserverA(BasSubject subject) : base(subject)
24         {
25             this.subject = subject;
26         }
27         public override async Task update()
28         {
29             if (this.subject.State == "interest")
30             {
31                 await Task.Run(() =>
32                 {
33                     Thread.Sleep(1000);
34                 });
35                 Console.WriteLine("interest change---from{0}", this.GetType());
36             }
37
38         }
39     }
40 //对象B
41     public class ObserverA : BasObserver
42     {
43         BasSubject subject;
44         public ObserverA()
45         {
46
47         }
48         public ObserverA(BasSubject subject) : base(subject)
49         {
50             this.subject = subject;
51         }
52         public override async Task update()
53         {
54             if (this.subject.State == "interest")
55             {
56                 await Task.Run(() =>
57                 {
58                     Thread.Sleep(1000);
59                 });
60                 Console.WriteLine("interest change---from{0}", this.GetType());
61             }
62
63         }
64     }

执行

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Subject subject = new Subject();
 6             ObserverA observerA1 = new ObserverA(subject);
 7             ObserverA observerA2 = new ObserverA();
 8             ObserverB observerB = new ObserverB(subject);
 9
10             subject.onChangeState("interest");
11             Console.WriteLine();
12             subject.onChangeState("un interest");
13             Console.ReadLine();
14         }
15     }

结果

该模式可实现独立的添加观察者,而无需修改目标对象和其他观察者对象。目标和观察者之间是通过抽象耦合的,观察者只需要实现指定的接口,便可得到目标对象的更新。

可改进的说明

  1. 目标发送通知时,并不知道有多少个对象需要更新,也不知道每个观察者的更新的具体实现,所以有些接口会耗时很长或者出现异常情况,这就需要我们在代码中实现异步和异常捕获操作。
  2. 目标发送通知时,可以通过把发生改变的详细信息通过参数方式传递给观察者,这称为推模型,但是不一定所有的观察者都需要这些的信息 。另外一种是目标对象仅发送最小信息发出,再由观察者去请求需要的信息,这叫拉模型。
  3. 观察多个对象跟观察感兴趣的改变,需要我们更改update接口接受目标实例和目标状态,用来判断是哪个目标发来的更新,和判断是不是感兴趣的更新。


应用场景

观察者模式最早应用于malltalk的mvc架构中。

其中model为目标对象,view为观察者,一个model可能会绑定到多个视图中,当model变化时需要同时改变各个view。

在项目开发中,我们提倡职责分离,所以我们可以添加一个controller层来处理响应用户输入,更新模型等功能

针对上面的例子加入控制器的代码如下

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var observer = new Observer();
 6             observer.submit();
 7             Console.ReadLine();
 8         }
 9     }
10
11     public class Controller {
12         Subject subject;
13         Observer observer;
14         /// <summary>
15         /// 注册观察者到目标
16         /// </summary>
17         /// <param name="observer"></param>
18         public Controller(Observer observer) {
19             this.observer = observer;
20             this.subject = new Subject();
21             subject.attachObserver(observer);
22         }
23         /// <summary>
24         /// 处理请求
25         /// </summary>
26         public void doSomeThing () {
27             subject.State = "interest";
28             subject.onChangeState();
29         }
30
31     }
32     public class Subject {
33         List<Observer> list;
34         public Subject() {
35             list = new List<Observer>();
36         }
37         public string State { get; set; }
38         public void attachObserver(Observer v)
39         {
40             list.Add(v);
41         }
42         public void onChangeState() {
43             list.ForEach(item =>
44             {
45                 item.update(this.State);
46             });
47         }
48     }
49     public class Observer {
50         Controller c;
51         public Observer() {
52             c = new Controller(this);
53         }
54         /// <summary>
55         /// 用户操作
56         /// </summary>
57         public void submit() {
58             c.doSomeThing();
59         }
60         /// <summary>
61         /// 刷新
62         /// </summary>
63         /// <param name="label"></param>
64         public void update(string label) {
65             Console.WriteLine(label);
66         }
67
68     }

在控制器中实现目标对象到观察者之间的映射。控制器收到视图发来的请求,去更新模型。模型变化会自动更新视图层

时间: 2024-10-18 01:31:51

javascript实现事件监控的相关文章

JavaScript中事件绑定的方法总结

最近收集了一些关于JavaScript绑定事件的方法,汇总了一下,不全面,但是,希望便于以后自己查看. JavaScript中绑定事件的方法主要有三种: 1 在DOM元素中直接绑定 2 JavaScript代码中直接绑定 3 绑定事件监听函数 一.在DOM元素中直接绑定 也就是直接在html标签中通过 onXXX=“” 来绑定.举个例子: <input type="button" value="点我呦" onclick="alert("he

javaScript 删除事件 弹出确认 取消对话框

1 javaScript 删除事件 弹出确认 取消对话框 2 1. 3 4 <a href="javascript:if(confirm('确实要删除?'))location='http://www.baidu.com'">删除</a> 5 6 2. 7 8 <script language="JavaScript"> 9 function delete_confirm(e) 10 { 11 if (event.srcElemen

JavaScript onkeydown事件入门实例(键盘某个按键被按下)

JavaScript onkeydown 事件 用户按下一个键盘按键时会触发 onkeydown 事件.与 onkeypress事件不同的是,onkeydown 事件是响应任意键按下的处理(包括功能键),onkeypress 事件只响应字符键按下后的处理. 提示 Internet Explorer/Chrome 浏览器使用 event.keyCode 取回被按下的字符,而 Netscape/Firefox/Opera 等浏览器使用 event.which. onkeydown 获取用户按下的键

JavaScript 阻止事件冒泡的实现方法

JavaScript 阻止事件冒泡,无使用其它插件来辅助,原生JS代码,考虑到浏览器的兼容性问题,这里对IE/火狐.Operating以及Chrome都有针对性的判断,代码如下: function cancelBubble(evt) { // 阻止事件冒泡 if (window.event) { // Chrome,IE6,Opera window.event.cancelBubble = true; } else { // FireFox 3 evt.stopPropagation(); }

JavaScript 自定义事件

Javascript事件模型系列(四)我所理解的javascript自定义事件 漫谈js自定义事件.DOM/伪DOM自定义事件

[转]javascript指定事件处理程序包括三种方式:

javascript指定事件处理程序包括三种方式: (1):DOM0级事件处理程序 如: 代码如下: var btn=document.getElementById("mybtn"); //取得该按钮的引用 btn.onclick=function(){ alert('clicked'); alert(this.id); // mybtn 以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理. 删除DOM0级方法指定的事件处理程序: btn.onclick=null; // 删除事件

JavaScript的事件_事件概述/事件模型

一.事件概述 JavaScript 事件是由访问 Web 页面的用户引起的一系列操作,例如:用户点击页面上的某个按钮或者鼠标移动到页面的某个图片上而产生一系列的互动的反馈. 当用户执行某些操作的时候,再去执行一系列代码. 事件一般是用于浏览器和用户操作进行交互.最早是 IE 和 Netscape Navigator 中出现,作为分担服务器端运算负载的一种手段.直到几乎所有的浏览器都支持事件处理. 而 DOM2级规范开始尝试以一种复合逻辑的方式标准化 DOM 事件.IE9.Firefox.Oper

JavaScript 计时事件

JavaScript 计时事件 通过使用 JavaScript,我们有能力作到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行.我们称之为计时事件. 在 JavaScritp 中使用计时事件是很容易的,两个关键方法是: setInterval() - 间隔指定的毫秒数不停地执行指定的代码. setTimeout() - 暂停指定的毫秒数后执行指定的代码 Note: setInterval() 和 setTimeout() 是 HTML DOM Window对象的两个方法. set

javascript的事件

原文:javascript的事件 前戏 今天在博客中看到了javascript的事件机制,就自己试试写一个简单的冒泡捕获测试,但是测试结果出乎了我的意料,主要是自己原来对事件了解不是很清楚,现在写篇博客记录下. 基础 先来看一下我在Aaron博客中截的一张图 DOM2中事件有三个阶段 事件的捕获阶段 事件的目标阶段 事件的冒泡阶段 事件的捕获阶段:是由document元素向发生事件的元素传递. 事件的目标阶段:是指在找到了发生事件的目标元素这个阶段,找到之后就会执行事件监听器.如果在目标元素上同