[修練之路-JS]4. JS’s Observer pattern (using closure)
續前一章"[修練之路-JS]3. JS’s Observer pattern (using prototype)"
並運用"[修練之路-JS]2. JS and jQuery's Module Design Pattern"
主要差別在於語法不同, 另外因為用了Module Design Pattern故會變成有點像是static的用法, 而非new一個object.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<script type="text/javascript" src="jshashtable.js"></script>
<script type="text/javascript">
// begin Subject ========
// 主題界面, 訂義訂閱(subscribe), 取消訂閱(unsubscribe), 發佈(notify/publish)
var Subject = (function(){
return {
subscribe : null,
unsubscribe : null,
notify : null
};
}());
// end Subject ========
// begin Location ========
// 位置類別, 實作訂閱(subscribe), 取消訂閱(unsubscribe), 發佈(notify/publish)
var Location = (function(s){
var l = {};
var observers = new Hashtable();
var current_id=0;
var subscribe=function(observer){
current_id++ ;
observers.put( current_id, observer );
return current_id;
};
var unsubscribe=function(id){
observers.remove(id);
};
var notify=function(x, y){
observers.each(function(key, func){
func.generate({ x : x, y : y });
});
};
l.subscribe = subscribe;
l.unsubscribe = unsubscribe;
l.notify = notify;
return l;
}(Subject));
// end Location ========
// begin Observer ========
// 觀察者界面, 訂義製作(generate)
var Observer = (function(){
return {
generate: null
};
}());
// end Observer ========
// begin CodeGenerator ========
// 觀察者-代碼建立, 觀察Location(x, y)值後建立代碼
var CodeGenerator = (function(o){
var c = {};
// override , 增加參數: [args[x, y,max_length, get_z, ctrl_id]]
// max_length: <int> 代碼長度
// get_attr_num: <function> 子類別提供代碼產生之數值參數
// ctrl_id: html dom object id
// 依參數 (x, y , get_z) 運算產生一個A-Z的字元, 並顯示於畫面中.
var generate = function(args){
if(this.code.length < this.max_length + 1 ){
var c = String.fromCharCode(65 + (args.x + args.y + this.get_z()) % 26);
this.code+=c;
document.getElementById(this.ctrl_id).innerText = this.code + " [xxx 未完成 xxx]";
}else{
document.getElementById(this.ctrl_id).innerText = this.code + " [ooo 已完成 ooo]";
}
}
var clear = function(){
document.getElementById(this.ctrl_id).innerText = "";
this.code = "";
}
c.generate=generate;
c.code="";
c.max_length = 8;
c.get_z = function(){ return 0 ;};
c.ctrl_id = "";
c.clear = clear;
return c;
}(Observer));
// end CodeGenerator ========
// begin TimeCodeGenerator ========
// 觀察者-依時間為參數產生代碼
var TimeCodeGenerator = (function(c){
// 繼承
var t = {}, key;
for( key in c ){
if(c.hasOwnProperty(key)){
t[key] = c[key];
}
}
var get_z=function(){
return new Date().getTime();
};
t.max_length=32;
t.get_z=get_z;
t.ctrl_id = "time_gener";
return t;
}(CodeGenerator));
// end TimeCodeGenerator ========
// begin RandomCodeGenerator ========
// 觀察者-依亂數為參數產生代碼
var RandomCodeGenerator = (function(c){
// 繼承
var r = {}, key;
for( key in c ){
if(c.hasOwnProperty(key)){
r[key] = c[key];
}
}
var get_z=function(){
return Math.random() * 100000 ;
};
r.max_length=16;
r.get_z=get_z;
r.ctrl_id = "random_gener";
return r;
}(CodeGenerator));
// end RandomCodeGenerator ========
// begin LocationTracker
// 處理畫面功能的啟動/關閉
var LocationTracker = (function(){
var start=function(){
end();
this.subjectId_01 = this.subject.subscribe( TimeCodeGenerator ) ;
this.subjectId_02 = this.subject.subscribe( RandomCodeGenerator ) ;
this.status=1;
document.getElementById('holder').style.display='block';
}
var end=function(){
TimeCodeGenerator.clear();
RandomCodeGenerator.clear();
if(this.subjectId_01){
this.subject.unsubscribe(this.subjectId_01);
this.subjectId_01=null;
document.getElementById("time_gener").innerText = "";
}
if(this.subjectId_02){
this.subject.unsubscribe(this.subjectId_02);
this.subjectId_02=null;
document.getElementById("random_gener").innerText = "";
}
this.status=0;
document.getElementById('holder').style.display='none';
}
return {
start: start,
end: end,
status: 0,
subject : Location,
subjectId_01 : null,
subjectId_02 : null
};
}());
// end LocationTracker ========
var tracker = LocationTracker;
</script>
</head>
<body onload="tracker.start()">
<input type="button" value="Start It" onclick="tracker.start();" />
<input type="button" value="End It" onclick="tracker.end();" /> <p />
<div id="holder">
請在空白處滑動你的mouse來產生代碼:
<div style="height:300px; width:300px; border-width:1px; border-style:solid" onmousemove="if(tracker.status==1) tracker.subject.notify(event.x, event.y)"></div>
<p>TimeCodeGenerator: <span id="time_gener" ></span> </p>
<p>RandomCodeGenerator: <span id="random_gener" ></span> </p>
</div>
</body>
</html>
在寫JavaScript 繼承時, 要很小心, 因為我連續 2 篇在實作時, 都犯了override 父類別方法時,
(1) 無窮迴圈, 因為父類別被子類別給覆蓋了, 表示只會呼自己.
(2) 父類別的方法被子類別不斷覆蓋, 結果只會保留最後一份方法.
(3) 忘了加 this.XXX (冏)
ps. 這次sample 無法在jsfiddle 上執行, 所以我就沒有放了.