說在前面:寫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/
二.不同設計模式實現之間的比較
命名約定
建議您始終以過去時態命名信號。這個慣例是基於AS3-Signals的建議,其背后的原因是避免與常規屬性混淆並保持一致性。嘗試組合多個單詞,如果您嘗試描述的事件沒有過去時的形式或如果您有命名沖突。
三.用法示例
1.引入js-signals:
node.js
var Signal = require('signals'); var mySignal = new Signal();
AMD
define(['signals'], function(Signal){ var mySignal = new Signal(); });
Browser globals
<script src="js/libs/signals.min.js"></script> <script> var Signal = signals.Signal; var mySignal = new Signal(); </script>
2.聲明Signal對象:
//store local reference for brevity var Signal = signals.Signal;
3.用戶自定義對象:
//custom object that dispatch signals var myObject = { started : new Signal(), //past tense is the recommended signal naming convention stopped : new Signal() };
4.添加/調度/刪除單個監聽器(Single Listener):給started 信號綁定事件 onstarted(function)
function onStarted(param1, param2){ alert(param1 + param2); } myObject.started.add(onStarted); //添加監聽器 myObject.started.dispatch('foo', 'bar'); //給 started 信號綁定的事件傳送參數(param1, param2),即('foo', 'bar') myObject.started.remove(onStarted); //刪除監聽器
5.添加/調度/刪除多個監聽器(Multiple Listeners)
function onStopped(){ alert('stopped'); } function onStopped2(){ alert('stopped listener 2'); } myObject.stopped.add(onStopped); myObject.stopped.add(onStopped2); myObject.stopped.dispatch(); myObject.stopped.removeAll(); //remove all listeners of the `stopped` signal
6.多次調度(Multiple Dispatches),也就是多次運行handler
var i = 0; myObject.started.add(function(){ i += 1; alert(i); }); myObject.started.dispatch(); //will alert 1,調度一次 myObject.started.dispatch(); //will alert 2,調度兩次
7.Multiple Dispatches + addOnce()
var i = 0; myObject.started.addOnce(function(){ i += 1; alert(i); }); myObject.started.dispatch(); //will alert 1 myObject.started.dispatch(); //nothing happens
8.啟用/禁用信號(Enable/Disable Signal)
var i = 0; myObject.started.add(function(){ i += 1; alert(i); }); myObject.started.dispatch(); //will alert 1 myObject.started.active = false; myObject.started.dispatch(); //nothing happens myObject.started.active = true; myObject.started.dispatch(); //will alert 2
9.Stop/Halt Propagation(停止/終止 傳播)
方法一:直接調用signal.halt()停止
myObject.started.add(function(){ myObject.started.halt(); //防止下一個listener在隊列中被執行
});
myObject.started.add(function(){ alert('second listener'); //不會被調用,因為第一個監聽器停止傳播
});
myObject.started.dispatch();
方法二:通過返回值 false 停止
myObject.started.add(function(){ return false; //if handler returns `false` will also stop propagation }); myObject.started.add(function(){ alert('second listener'); //won't be called since first listener stops propagation }); myObject.started.dispatch();
10.設置偵聽器處理程序的執行上下文
var foo = 'bar'; var obj = { foo : 10 }; function handler1(){ alert(this.foo); } function handler2(){ alert(this.foo); } //note that you cannot add the same handler twice to the same signal without removing it first myObject.started.add(handler1); //默認執行上下文,參數是windows.foo myObject.started.add(handler2, obj); //設置不同的上下文,傳入obj.foo myObject.started.dispatch(); //first handler will alert "bar", second will alert "10".
11.設置監聽器的優先級
var handler1 = function(){ alert('foo'); }; var handler2 = function(){ alert('bar'); }; myObject.started.add(handler1); //默認優先級為0,add(handler,context,priority) myObject.started.add(handler2, null, 2); //設置優先級為2,'handler2' 將比'handler1' 提早運行 myObject.started.dispatch(); //will alert "bar" than "foo"
12.啟用/禁用單個SignalBinding
var handler1 = function(){ alert('foo bar'); }; var handler2 = function(){ alert('lorem ipsum'); }; var binding1 = myObject.started.add(handler1); //methods `add()` and `addOnce()` returns a SignalBinding object myObject.started.add(handler2); myObject.started.dispatch(); //execute handler1 , will alert "foo bar" than "lorem ipsum" binding1.active = false; //disable a single binding myObject.started.dispatch(); //will alert "lorem ipsum" binding1.active = true; myObject.started.dispatch(); //will alert "foo bar" than "lorem ipsum"
13.手動執行信號處理程序:不用signal.dispatch(),用 SignalBinding.execute()
var handler = function(){ alert('foo bar'); }; var binding = myObject.started.add(handler); //methods `add()` and `addOnce()` returns a SignalBinding object binding.execute(); //will alert "foo bar"
14.檢索匿名監聽器:本人覺得可能是系統默認綁定的handler
var binding = myObject.started.add(function(){ alert('foo bar'); }); var handler = binding.getListener(); //reference to the anonymous function
15.刪除/分離匿名監聽器
var binding = myObject.started.add(function(){ alert('foo bar'); }); myObject.started.dispatch(); //will alert "foo bar" binding.detach();//分離監聽器 alert(binding.isBound()); //will alert `false` myObject.started.dispatch(); //nothing happens
16.檢查綁定是否只執行一次
var binding1 = myObject.started.add(function(){ alert('foo bar'); }); var binding2 = myObject.started.addOnce(function(){ alert('foo bar'); }); alert(binding1.isOnce()); //alert "false" alert(binding2.isOnce()); //alert "true"
17.更改監聽器執行上下文
var foo = 'bar'; var obj = { foo : "it's over 9000!" }; var binding = myObject.started.add(function(){ alert(this.foo);//this 指的是 windows,也就是說默認綁定的上下文環境是windows }); myObject.started.dispatch(); //will alert "bar" binding.context = obj; myObject.started.dispatch(); //will alert "it's over 9000!"
18.將默認參數添加到信號調度
var binding = myObject.started.add(function(a, b, c){ alert(a +' '+ b +' '+ c); }); binding.params = ['lorem', 'ipsum']; //set default parameters of the binding myObject.started.dispatch('dolor'); //will alert "lorem ipsum dolor"
類似於:
var binding = myObject.started.add(function(a, b, c){ alert(a +' '+ b +' '+ c); }); myObject.started.dispatch('lorem', 'ipsum','dolor'); //will alert "lorem ipsum dolor"
19.檢查信號是否綁定某個特定的監聽器
function onStart(a){ console.log(a); } myObject.started.add(onStart); myObject.started.has(onStart); // true
20.記住以前分派的值/忘記值
myObject.started.memorize = true; // default is false myObject.started.dispatch('foo'); // add()/addOnce() will automatically fire listener if signal was dispatched before // will log "foo" since it keeps record of previously dispatched values myObject.started.addOnce(console.log, console); // dispatching a new value will overwrite the "memory" myObject.started.dispatch('lorem'); // will log "lorem" myObject.started.addOnce(console.log, console); myObject.started.forget(); // forget previously dispatched values (reset signal state) myObject.started.addOnce(console.log, console); // won't log till next dispatch (since it "forgot") myObject.started.dispatch('bar'); // log "bar"
21.一次性監聽多個信號見 CompoundSignal repository
四.庫函數解析
1. signals ,Signals命名空間
2.Signal類
var mySignal = new Signal();//mySignal 是 Signals 類的一個對象。
Signal類屬性
mySignal.active = false/true; //該信號監聽調度是否停止,默認true可用 mySignal.memorize = false/true; //該信號是否記錄之前的調度參數並且自動執行,默認為false不記錄 mySignal.VERSION = ''; //記錄當前信號的版本
Signals類方法
add();添加監聽器
//add(listener, listenerContext, priority); //listener 屬於function類型,是信號操作函數 //listenerContext 屬於object類型,是監聽器的執行上下文環境,可選 //priority 屬於Number類型,是該監聽器的優先級,高優先級的先執行,默認為0,可選
//返回值是 SignalBinding 類型
mySignal.add(listener, listenerContext, priority);
addOnce();添加一次性監聽器,運行完一次即可自動remove
//add(listener, listenerContext, priority); //listener 屬於function類型,是信號操作函數 //listenerContext 屬於object類型,是監聽器的執行上下文環境,可選 //priority 屬於Number類型,是該監聽器的優先級,高優先級的先執行,默認為0,可選 //返回值是 SignalBinding 類型 mySignal.addOnce(listener, listenerContext, priority);
dispatch();調度/廣播信號到所有監聽器,加入隊列,即讓監聽器運行
//dispatch(params),paras可選 mySignal.dispatch();
dispose();刪除所有相關的信號對象和綁定,無返回值
mySignal.dispose();
forget();清除記憶的arguments,無返回值
mySignal.forget();
getNumListeners();返回該信號綁定的監聽器的個數
mySignal.getNumListeners();
halt();停止分發事件,blocking the dispatch to next listeners on the queue.
mySignal.halt();
has();檢查該信號是否與特定監聽器綁定
//listener ; 監聽器的執行函數 //context ;執行的上下文環境,可選 // 返回值為true 或者 false mySignal.has(listener, context);
remove();刪除監聽器
//listener ; 監聽器的執行函數 //context ;執行的上下文環境,可選 //返回監聽器的執行函數handler mySignal.remove(listener, context);
removeAll();刪除該信號的所有監聽器
mySignal.removeAll();
toString();返回當前的object的
3.SignalBinding類
var binding = myObject.started.add(function(){ alert('foo bar'); });
等同於
//SignalBinding(signal, listener, isOnce, listenerContext(可選), priority(可選))
var binding = new SignalBinding(myObject.started, handler, false, myObject, 0) function handler(){ alert('foo bar'); }
SignalBinding類屬性
binding.active = false/true; //該信號監聽調度是否停止,默認true可用 binding.context = object; //綁定的上下文環境 binding.params = ''; //默認在Signal.dispatch()期間傳遞給監聽器的參數
SignalBinding類方法
binding.detach();//分離信號和監聽器 binding.execute();//手動執行 binding.getListener();//返回綁定的特定監聽函數 binding.getSignal();//返回綁定的特定信號量 binding.isBound();//判斷是否綁定成功,true or false binding.isOnce();//判斷是否是一次綁定 binding.toString()//轉換為字符串
函數簡易查詢索引地址:https://millermedeiros.github.io/js-signals/docs/symbolindex.html
五.應用實例
<!DOCTYPE html> <html> <head> <title>test signal</title> <script type="text/javascript" src="signals.js"></script> </head> <body> <script type="text/javascript"> //custom object that defines a `started` signal var myObject = { started: new signals.Signal() }; // a handler function function onStarted(param1, param2){ alert(param1 + param2); } // listen for the `started` signal and then run the `onStarted` handler myObject.started.add(onStarted); //add listener // dispatch the `started` signal, running the handler, passing in parameters myObject.started.dispatch('foo', 'bar'); </script> </body> </html>