说在前面:写js时候,当一个变量一旦发生变化,就自动执行相应的程序,而不用手动执行,js-signals可以很方便的解决这个问题。

一.js-signals简介

  js-signals是用于在JavaScript中的软件组件之间定义和触发基于事件的消息的库,它是一个类似于Event Emitter / Dispatcher或Pub / Sub系统的事件/消息系统,主要区别在于每个事件类型都有自己的控制器功能,而不依赖于字符串来广播/订阅事件,它还具有通常在其他系统上不可用的一些额外功能。

  本人因为项目需要,寻找关于js-signals的学习资料,发现都是英文的,所以自己写个学习小结,也希望帮助后来需要学习的同学们。后面第三部分用户指南,会详细介绍js-signals的具体用法。

github:https://github.com/millermedeiros/js-signals

example:http://www.javascriptoo.com/js-signals

官方使用详解:https://millermedeiros.github.io/js-signals/

官方documentation:https://millermedeiros.github.io/js-signals/docs/

作者博客:http://blog.millermedeiros.com/

http://blog.millermedeiros.com/js-signals-custom-eventmessaging-system-for-javascript/

二.不同设计模式实现之间的比较

  下面的比较只是订阅事件类型、调度和删除事件侦听器的基本功能。它不是基于可用的功能,而是基于每个概念的基本实现与使用每个概念的利弊之间的差异。但通常在于大多数情况下,“智能实施”和“黑客”可以避免列出的一些缺点。

  所有的实现完成相同的任务,并且基于相同的设计模式(Observer),它们也有许多共同之处,但运行方式上却有些许不同。本文主要是为了帮助你选择哪种实现最适合你的工作流以及你要解决的问题种类。

第一种模式:事件发射器/目标/调度器(Event Emitter/Target/Dispatcher)

  • 调度自定义事件的每个对象都需要继承自EventEmitter / EventTarget / EventDispatcher对象或实现正确的接口。
  • 使用字符串来定义事件类型。
  • DOM2/DOM3 Events基于这样的模式。

代码示例

  1. myObject.addEventListener('myCustomEventTypeString', handler);
  2. myObject.dispatchEvent(new Event('myCustomEventTypeString'));
  3. myObject.removeEventListener('myCustomEventTypeString', handler);

优点

  • 完全控制 target object ,并确保只监听特定目标发送的事件。
  • 可以调度任意事件类型而不修改目标对象。
  • 对于每种 target/object/event 都使用相同的方法。
  • 易理解代码。
  • 事件 target 通常是 object 本身,这使得事件冒泡更有逻辑。事件冒泡指的是在一个对象上触发某类事件(比如单击onclick事件),如果此对象定义了此事件的处理程序,那么此事件就会调用这个处理程序,如果没有定义此事件处理程序或者事件返回true,那么这个事件会向这个对象的父级对象传播,从里到外,直至它被处理(父级对象所有同类事件都将被激活),或者它到达了对象层次的最顶层,即document对象(有些浏览器是window)。
  • 流行。

缺点

  • 倾向于使用继承而不是组合。。
  • 使用字符串来定义事件类型,容易出现拼写错误并且 auto-complete 无法正常工作。
  • 事件处理程序通常只接受一个参数(Event Object)。
    • 如果你想传递额外的数据,你必须创建一个自定义的事件对象来实现正确的接口或扩展一个基本的事件对象,这个过程通常是复杂的。

第二种模式:发布/订阅(pub / sub)

  • 使用单个对象向多个订户广播消息

    • 不是必要条件,但大多数实现都使用静态集中对象作为广播者。
  • 使用字符串来定义事件类型。
  • 消息与事件的目标之间没有任何关系。

代码示例

  1. globalBroadcaster.subscribe('myCustomEventTypeString', handler);
  2. globalBroadcaster.publish('myCustomEventTypeString', param1, param2, ...);
  3. globalBroadcaster.unsubscribe('myCustomEventTypeString', handler);

优点

  • 任何对象都可以发布/订阅任何事件类型。
  • 轻巧。
  • 易于使用/实现。

缺点

  • 任何对象都可以发布/订阅任何事件类型。(是的,它是pro也是con)
  • 使用字符串来定义事件类型。
    • 容易出错。
    • 代码不能自动完成(除非您将value作为变量/常量存储)。
    • 通过命名规范来避免消息被错误的订阅者拦截

第三种模式:信号(js-signals)

  • 每个事件类型都有自己的控制器
  • 事件类型不依赖于字符串。

代码示例

  1. myObject.myCustomEventType.add(handler);
  2. myObject.myCustomEventType.dispatch(param1, param2, ...);
  3. myObject.myCustomEventType.remove(handler);

优点

  • 不依赖字符串。

    • 代码完成友好。
    • 尝试调度或收听不存在的事件类型会引发错误(有助于您早点找到错误)。
    • 不需要创建常量来存储字符串值。
  • 对每个听众和事件类型进行粒度控制。
    • 每个信号都有一个特定的目标/容器。
  • 容易定义对象派发的signal。
  • 轻松控制事件广播和订户,避免错误的对象对事件做出反应。
  • 事件处理程序可以接受任意数量的参数。
  • 通常在观察者模式的其他实现上不存在的便利方法,如:
    • 启用/禁用每个事件类型的事件分派。
    • 删除附加到特定事件类型的所有事件侦听器。
    • 第一次执行后自动删除侦听器的选项。
    • 将执行上下文绑定到事件处理程序,避免作用域问题。
    • 删除/分离匿名侦听器。
    • 等等...
  • 倾向于使用组合而不是继承。
    • 不会混淆原型链。

缺点

  • 无法调度任意事件。(在大多数情况下也是优点)
  • 每个事件类型是一个对象成员。(在大多数情况下也是优点)
    • 如果有多个事件类型,会导致命名空间混乱
  • 不会将事件类型和目标对象传递给callback使得很难使用通用的handler(工作于多个事件类型和目标)。
  • 与大多数人习惯不同。

命名约定

建议您始终以过去时态命名信号。这个惯例是基于AS3-Signals的建议,其背后的原因是避免与常规属性混淆并保持一致性。尝试组合多个单词,如果您尝试描述的事件没有过去时的形式或如果您有命名冲突。

三.用法示例

1.引入js-signals:

node.js

  1. var Signal = require('signals');
  2. var mySignal = new Signal();

AMD

  1. define(['signals'], function(Signal){
  2. var mySignal = new Signal();
  3. });

Browser globals

  1. <script src="js/libs/signals.min.js"></script>
  2. <script>
  3. var Signal = signals.Signal;
  4. var mySignal = new Signal();
  5. </script>

2.声明Signal对象:

  1. //store local reference for brevity
  2. var Signal = signals.Signal;

3.用户自定义对象:

  1. //custom object that dispatch signals
  2. var myObject = {
  3. started : new Signal(), //past tense is the recommended signal naming convention
  4. stopped : new Signal()
  5. };

4.添加/调度/删除单个监听器(Single Listener):给started 信号绑定事件 onstarted(function)

  1. function onStarted(param1, param2){
  2. alert(param1 + param2);
  3. }
  4. myObject.started.add(onStarted); //添加监听器
  5. myObject.started.dispatch('foo', 'bar'); //给 started 信号绑定的事件传送参数(param1, param2),即('foo', 'bar')
  6. myObject.started.remove(onStarted); //删除监听器

5.添加/调度/删除多个监听器(Multiple Listeners)

  1. function onStopped(){
  2. alert('stopped');
  3. }
  4. function onStopped2(){
  5. alert('stopped listener 2');
  6. }
  7. myObject.stopped.add(onStopped);
  8. myObject.stopped.add(onStopped2);
  9. myObject.stopped.dispatch();
  10. myObject.stopped.removeAll(); //remove all listeners of the `stopped` signal

6.多次调度(Multiple Dispatches),也就是多次运行handler

  1. var i = 0;
  2. myObject.started.add(function(){
  3. i += 1;
  4. alert(i);
  5. });
  6. myObject.started.dispatch(); //will alert 1,调度一次
  7. myObject.started.dispatch(); //will alert 2,调度两次

7.Multiple Dispatches + addOnce()

  1. var i = 0;
  2. myObject.started.addOnce(function(){
  3. i += 1;
  4. alert(i);
  5. });
  6. myObject.started.dispatch(); //will alert 1
  7. myObject.started.dispatch(); //nothing happens

8.启用/禁用信号(Enable/Disable Signal)

  1. var i = 0;
  2. myObject.started.add(function(){
  3. i += 1;
  4. alert(i);
  5. });
  6. myObject.started.dispatch(); //will alert 1
  7. myObject.started.active = false;
  8. myObject.started.dispatch(); //nothing happens
  9. myObject.started.active = true;
  10. myObject.started.dispatch(); //will alert 2

9.Stop/Halt Propagation(停止/终止 传播)

方法一:直接调用signal.halt()停止

  1. myObject.started.add(function(){
  2. myObject.started.halt(); //防止下一个listener在队列中被执行
  1. });
    myObject.started.add(function(){ alert('second listener'); //不会被调用,因为第一个监听器停止传播
  1. });
    myObject.started.dispatch();

方法二:通过返回值 false 停止

  1. myObject.started.add(function(){
  2. return false; //if handler returns `false` will also stop propagation
  3. });
  4. myObject.started.add(function(){
  5. alert('second listener'); //won't be called since first listener stops propagation
  6. });
  7. myObject.started.dispatch();

10.设置侦听器处理程序的执行上下文

  1. var foo = 'bar';
  2. var obj = {
  3. foo : 10
  4. };
  5.  
  6. function handler1(){
  7. alert(this.foo);
  8. }
  9. function handler2(){
  10. alert(this.foo);
  11. }
  12. //note that you cannot add the same handler twice to the same signal without removing it first
  13. myObject.started.add(handler1); //默认执行上下文,参数是windows.foo
  14. myObject.started.add(handler2, obj); //设置不同的上下文,传入obj.foo
  15. myObject.started.dispatch(); //first handler will alert "bar", second will alert "10".

11.设置监听器的优先级

  1. var handler1 = function(){
  2. alert('foo');
  3. };
  4. var handler2 = function(){
  5. alert('bar');
  6. };
  7. myObject.started.add(handler1); //默认优先级为0,add(handler,context,priority)
  8. myObject.started.add(handler2, null, 2); //设置优先级为2,'handler2' 将比'handler1' 提早运行
  9. myObject.started.dispatch(); //will alert "bar" than "foo"

12.启用/禁用单个SignalBinding

  1. var handler1 = function(){
  2. alert('foo bar');
  3. };
  4. var handler2 = function(){
  5. alert('lorem ipsum');
  6. };
  7. var binding1 = myObject.started.add(handler1); //methods `add()` and `addOnce()` returns a SignalBinding object
  8. myObject.started.add(handler2);
  9. myObject.started.dispatch(); //execute handler1 , will alert "foo bar" than "lorem ipsum"
  10. binding1.active = false; //disable a single binding
  11. myObject.started.dispatch(); //will alert "lorem ipsum"
  12. binding1.active = true;
  13. myObject.started.dispatch(); //will alert "foo bar" than "lorem ipsum"

13.手动执行信号处理程序:不用signal.dispatch(),用 SignalBinding.execute()

  1. var handler = function(){
  2. alert('foo bar');
  3. };
  4. var binding = myObject.started.add(handler); //methods `add()` and `addOnce()` returns a SignalBinding object
  5. binding.execute(); //will alert "foo bar"

14.检索匿名监听器:本人觉得可能是系统默认绑定的handler

  1. var binding = myObject.started.add(function(){
  2. alert('foo bar');
  3. });
  4. var handler = binding.getListener(); //reference to the anonymous function

15.删除/分离匿名监听器

  1. var binding = myObject.started.add(function(){
  2. alert('foo bar');
  3. });
  4. myObject.started.dispatch(); //will alert "foo bar"
  5. binding.detach();//分离监听器
  6. alert(binding.isBound()); //will alert `false`
  7. myObject.started.dispatch(); //nothing happens

16.检查绑定是否只执行一次

  1. var binding1 = myObject.started.add(function(){
  2. alert('foo bar');
  3. });
  4. var binding2 = myObject.started.addOnce(function(){
  5. alert('foo bar');
  6. });
  7. alert(binding1.isOnce()); //alert "false"
  8. alert(binding2.isOnce()); //alert "true"

17.更改监听器执行上下文

  1. var foo = 'bar';
  2. var obj = {
  3. foo : "it's over 9000!"
  4. };
  5. var binding = myObject.started.add(function(){
  6. alert(this.foo);//this 指的是 windows,也就是说默认绑定的上下文环境是windows
  7. });
  8. myObject.started.dispatch(); //will alert "bar"
  9. binding.context = obj;
  10. myObject.started.dispatch(); //will alert "it's over 9000!"

18.将默认参数添加到信号调度

  1. var binding = myObject.started.add(function(a, b, c){
  2. alert(a +' '+ b +' '+ c);
  3. });
  4. binding.params = ['lorem', 'ipsum']; //set default parameters of the binding
  5. myObject.started.dispatch('dolor'); //will alert "lorem ipsum dolor"

类似于:

  1. var binding = myObject.started.add(function(a, b, c){
  2. alert(a +' '+ b +' '+ c);
  3. });
  4. myObject.started.dispatch('lorem', 'ipsum''dolor'); //will alert "lorem ipsum dolor"

19.检查信号是否绑定某个特定的监听器

  1. function onStart(a){
  2. console.log(a);
  3. }
  4. myObject.started.add(onStart);
  5. myObject.started.has(onStart); // true

20.记住以前分派的值/忘记值

  1. myObject.started.memorize = true; // default is false
  2. myObject.started.dispatch('foo');
  3.  
  4. // add()/addOnce() will automatically fire listener if signal was dispatched before
  5. // will log "foo" since it keeps record of previously dispatched values
  6. myObject.started.addOnce(console.log, console);
  7.  
  8. // dispatching a new value will overwrite the "memory"
  9. myObject.started.dispatch('lorem');
  10. // will log "lorem"
  11. myObject.started.addOnce(console.log, console);
  12.  
  13. myObject.started.forget(); // forget previously dispatched values (reset signal state)
  14. myObject.started.addOnce(console.log, console); // won't log till next dispatch (since it "forgot")
  15. myObject.started.dispatch('bar'); // log "bar"

21.一次性监听多个信号见 CompoundSignal repository

四.库函数解析

1. signals ,Signals命名空间

2.Signal类

  1. var mySignal = new Signal();//mySignal 是 Signals 类的一个对象。

Signal类属性

  1. mySignal.active = false/true; //该信号监听调度是否停止,默认true可用
  2. mySignal.memorize = false/true; //该信号是否记录之前的调度参数并且自动执行,默认为false不记录
  3. mySignal.VERSION = ''; //记录当前信号的版本

Signals类方法

add();添加监听器

  1. //add(listener, listenerContext, priority);
  2. //listener 属于function类型,是信号操作函数
  3. //listenerContext 属于object类型,是监听器的执行上下文环境,可选
  4. //priority 属于Number类型,是该监听器的优先级,高优先级的先执行,默认为0,可选
    //返回值是 SignalBinding 类型
  5. mySignal.add(listener, listenerContext, priority);

addOnce();添加一次性监听器,运行完一次即可自动remove

  1. //add(listener, listenerContext, priority);
  2. //listener 属于function类型,是信号操作函数
  3. //listenerContext 属于object类型,是监听器的执行上下文环境,可选
  4. //priority 属于Number类型,是该监听器的优先级,高优先级的先执行,默认为0,可选
  5. //返回值是 SignalBinding 类型
  6. mySignal.addOnce(listener, listenerContext, priority);

dispatch();调度/广播信号到所有监听器,加入队列,即让监听器运行

  1. //dispatch(params),paras可选
  2. mySignal.dispatch();

dispose();删除所有相关的信号对象和绑定,无返回值

  1. mySignal.dispose();

forget();清除记忆的arguments,无返回值

  1. mySignal.forget();

getNumListeners();返回该信号绑定的监听器的个数

  1. mySignal.getNumListeners();

halt();停止分发事件,blocking the dispatch to next listeners on the queue.

  1. mySignal.halt();

has();检查该信号是否与特定监听器绑定

  1. //listener ; 监听器的执行函数
  2. //context ;执行的上下文环境,可选
  3. // 返回值为true 或者 false
  4. mySignal.has(listener, context);

remove();删除监听器

  1. //listener ; 监听器的执行函数
  2. //context ;执行的上下文环境,可选
  3. //返回监听器的执行函数handler
  4. mySignal.remove(listener, context);

removeAll();删除该信号的所有监听器

  1. mySignal.removeAll();

toString();返回当前的object的

3.SignalBinding类

  1. var binding = myObject.started.add(function(){
  2. alert('foo bar');
  3. });

等同于

  1. //SignalBinding(signal, listener, isOnce, listenerContext(可选), priority(可选))
    var binding = new SignalBinding(myObject.started, handler, false, myObject, 0)
  2. function handler(){
  3. alert('foo bar');
  4. }

SignalBinding类属性

  1. binding.active = false/true; //该信号监听调度是否停止,默认true可用
  2. binding.context = object; //绑定的上下文环境
  3. binding.params = ''; //默认在Signal.dispatch()期间传递给监听器的参数

SignalBinding类方法

  1. binding.detach();//分离信号和监听器
  2. binding.execute();//手动执行
  3. binding.getListener();//返回绑定的特定监听函数
  4. binding.getSignal();//返回绑定的特定信号量
  5. binding.isBound();//判断是否绑定成功,true or false
  6. binding.isOnce();//判断是否是一次绑定
  7. binding.toString()//转换为字符串

函数简易查询索引地址:https://millermedeiros.github.io/js-signals/docs/symbolindex.html

五.应用实例

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>test signal</title>
  5. <script type="text/javascript" src="signals.js"></script>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9.  
  10. //custom object that defines a `started` signal
  11. var myObject = {
  12. started: new signals.Signal()
  13. };
  14.  
  15. // a handler function
  16. function onStarted(param1, param2){
  17. alert(param1 + param2);
  18. }
  19.  
  20. // listen for the `started` signal and then run the `onStarted` handler
  21. myObject.started.add(onStarted); //add listener
  22.  
  23. // dispatch the `started` signal, running the handler, passing in parameters
  24. myObject.started.dispatch('foo', 'bar');
  25.  
  26. </script>
  27. </body>
  28. </html>

js-signals学习以及应用的更多相关文章

  1. js面向对象学习 - 对象概念及创建对象

    原文地址:js面向对象学习笔记 一.对象概念 对象是什么?对象是“无序属性的集合,其属性可以包括基本值,对象或者函数”.也就是一组名值对的无序集合. 对象的特性(不可直接访问),也就是属性包含两种,数 ...

  2. js数组学习整理

    原文地址:js数组学习整理 常用的js数组操作方法及原理 1.声明数组的方式 var colors = new Array();//空的数组 var colors = new Array(3); // ...

  3. js入门学习~ 运动应用小例

    要实现的效果如下: 鼠标移入各个小方块,实现对应的效果(变宽,变高,移入透明,移出恢复)~~ (且各运动相互之前不干扰)  主要是练习多个物体的运动框架~~ --------------------- ...

  4. JS入门学习,写一个时钟~

    <!-- 耽搁了几天,于是又继续回到JS的学习了~~ 各种头大,加油吧... --> <!doctype html><html><head> <t ...

  5. 【干货分享】Node.js 中文学习资料和教程导航

    这篇文章来自 Github 上的一位开发者收集整理的 Node.js 中文学习资料和教程导航.Node 是一个服务器端 JavaScript 解释器,它将改变服务器应该如何工作的概念,它的目标是帮助程 ...

  6. js再学习笔记

    #js再学习笔记 ##基本 1.js严格区分大小写   2.js末尾的分号可加,也可不加   3.六种数据类型(使用typeof来检验数据的类型) `typeof` - undefined: `var ...

  7. JS数组学习笔记

    原文:JS数组学习笔记 最近在备课数组,发现很多ES5的方法平时很少用到.细节比较多,自己做了大量例子和整理,希望对大家了解JavaScript中的Array有所帮助. 概念 数组是值的有序集合.每个 ...

  8. js 正则学习小记之匹配字符串

    原文:js 正则学习小记之匹配字符串 今天看了第5章几个例子,有点收获,记录下来当作回顾也当作分享. 关于匹配字符串问题,有很多种类型,今天讨论 js 代码里的字符串匹配.(因为我想学完之后写个语法高 ...

  9. js 正则学习小记之左最长规则

    原文:js 正则学习小记之左最长规则 昨天我在判断正则引擎用到的方法是用 /nfa|nfa not/ 去匹配 "nfa not",得到的结果是 'nfa'.其实我们的本意是想得到整 ...

  10. js 正则学习小记之NFA引擎

    原文:js 正则学习小记之NFA引擎 之前一直认为自己正则还不错,在看 次碳酸钴,Barret Lee 等大神都把正则玩的出神入化后发现我只是个战五渣.  求抱大腿,求大神调教. 之前大致有个印象,正 ...

随机推荐

  1. 关于DDOS的主动与智能防御

    -   善守者藏于九地之下    - -  狡兔九窟 - 一.分配足够多的网关服务器 让用户总一个终点,可以进入游戏.多分配,动态分配,定期更新 二.用户分组分级 分组可以根据用户的生成时间, 在线时 ...

  2. 【tomcat】启动报错:Failed to initialize end point associated with ProtocolHandler ["http-apr-8080"] java.lang.Exception: Socket bind failed 和java.net.BindException: Address already in use: JVM_Bind错误解决

    背景:[新手] 将开发机子上的Tomcat连同其中的项目,一起拷贝到服务器上,启动tomcat的start.bat,然后报错如下: 问题1: Failed to initialize end poin ...

  3. getAttribLocation的返回值

    var coord = gl.getAttribLocation(shaderProgram, "coordinates");    // 0 var coord2 = gl.ge ...

  4. php-fpm高并发配置[1000+]

    Dell R430 2个物理CPU,每个CPU有6个内核: www.conf: pm = dynamic pm.max_children = 120 pm.start_servers = 8 pm.m ...

  5. 初学html的单词笔记

    font-size: 文字大小color: 顏色solid: 边框线text-align: 間距center: 文字放在中間<head> 网页头部<title> 网页标题< ...

  6. github如何删除新建仓库(致新手)

    github作为开发人员的必备用具.那么,作为一个新手如何删除github中建立的仓库呢? 1.以删除My test为例

  7. angular的点击添加

    首先是在js里面我们可以用clone来点击添加一些东西比如列表或者其他的div之类的,但是在angular里面怎么实现点击添加呢? 类似这种: 这样就尴尬了,最少我这样的菜鸟是不知道怎么去写的,网上好 ...

  8. Tree Recovery

    #include<stdio.h> #include<string.h> void build(int n,char*s1,char*s2) { )return ; ])-s2 ...

  9. C++数组排序

    #include<stdio.h> #include<stdlib.h> #include<windows.h> #define SIZE 5 //数组中元素的数量 ...

  10. Javascript中的 “&” 和 “|” 详解

    转自:https://www.jb51.net/article/104394.htm 一.前言: 在文章开始之前,先出几个题目给大家看看: var num1 = 1 & 0; console. ...