channel模塊式所有模塊中比較核心的一個模塊,它定義一個通道,在這個通道上有一個事件,你可以訂閱這個事件,當這個事件被激發的時候,所有註冊在這個通道上的訂閱者都能收到。這裏的訂閱者其實就是一個函數,當事件激發,函數就會被調用。
下面是channel的工廠函數的主體代碼:
function(require, exports, module) {
var utils = require('myphonegap/utils');
var Channel = function(type, opts) {
},
channel = {
};
function forceFunction(f) {
}
Channel.prototype.subscribe = function(f, c, g) {
};
Channel.prototype.subscribeOnce = function(f, c) {
};
Channel.prototype.unsubscribe = function(g) {
};
Channel.prototype.fire = function(e) {
};
channel.create('onDOMContentLoaded');
channel.create('onNativeReady');
channel.create('onMyphonegapReady');
channel.create('onMyphonegapInfoReady');
channel.create('onMyphonegapConnectionReady');
channel.create('onDeviceReady');
channel.create('onResume');
channel.create('onPause');
channel.create('onDestroy');
channel.waitForInitialization('onMyphonegapReady');
channel.waitForInitialization('onMyphoneConnectionReady');
module.exports = channel;
}
上面的代碼是JS中比較經典的實現的面向對象的方法:將成員變量定義在構造函數中,將成員函數定義在構造函數的原型對象中。這樣通過new方法實例出來的對象,成員變量是相互獨立的,而成員函數通過原型對象共享。
Channel這個函數對象可以看做是一個通道類的構造函數,最重要的兩個成員變量type存儲事件類型,handlers存儲事件訂閱者(函數)。
var Channel = function(type,sticky){
this.type = type;
//map of guid -> function
this.handlers = {};
// 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
this.state = sticky?1:0;
// Used in sticky mode to remember args passed to fire().
this.fireArgs = null;
// Used by onHasSubscribersChange to know if there are any listeners.
this.numHandlers = 0;
// Function that is called when the first listener is subscribed, or when
// the last listener is unsubscribed.
this.onHasSubscribersChange = null;
}
this指針指向的是當前執行環境,所以通過new構造新的對象的時候,this指向的就是這個新生成的對象,而之後函數原型對象中的函數執行的時候,函數中的this指針執行的就是調用它們的對象。但是Channel當做一個函數執行時,this指針指向的就是全局執行環境,也就是window這個對象,調用Channel也不能返回一個對象。
下面再分析下channel這個對象,注意這個channel是小寫字母開頭。先上代碼:
channel={
create:function(type){
channel[type] = new Channel(type,false);
},
createSticky:function(type){
channel[type] = new Channel(type,true);
},
deviceChannelsArray:[],
deviceChannelsMap:{},
waitForInitialization:function(feature){
if(feature){
var c = channel[feature]||this.createSticky(feature);
this.deviceChannelsArray.push(c);
this.deviceChannelsMap[feature]=c;
}
},
initializationComplete:function(feature){
var c = this.deviceChannelsMap[feature];//
if(c){
c.fire();
}
},
join: function (h, c) {//join也就是將一組Channel連接起來,並注入一個在所有Channel上只執行一次的公共處理函數
var i = c.length;
var len = i;
var f = function() {
if (!(--i)) h();
};
for (var j=0; j<len; j++) {
!c[j].fired?c[j].subscribeOnce(f):i--;
}
if (!i) h();
}
};
如果只看名字,一般人都以爲channel是Channel的一個實例,我開始也是這樣認爲的;而事實上它根本不是Channel的實例,如果勉強一點可以把它看做Channel實例的集合,之所以說勉強,是因爲它還提供了一些函數。我想之所以它也取名叫做channel而不叫channelMap或channels的原因是它最終是作爲通道模塊的導出對象的。
這個channel對象的功能:通過create方法構造各個通道實例,然後可以動過這個對象訪問到他已生成的對象實例。比如channel.create('onDeviceReady')創建通道實例之後,就可以通過channel.onDeviceReady來訪問這個Channel實例了,每個Channel提供了subscribe、unsubscribe、subscribeOnce、fire四個方法。
如果採用其他語言實現channl這個對象功能,就會寫一個ChannelManager的單例。它提供一個create方法,通過不同的通道名生成Channel實例,然後將通道名與實例作爲一個鍵值對存儲在Map中,通過一個get函數來訪問對應的Channel實例。
ChannelManager.getInstance().create(“onDeviceReady”);
Channel onDeviceReady = ChannelManager.getInstance().get(“onDeviceReady”);
至於channel提供的waitForInitialization、和initializationComplete這兩個函數,一個是等待初始化,一個是初始化完成。等待初始化將根據名稱實例化通道加入到一個表中,調用初始化完成函數就激發這些通道事件。
join函數很費解,暫不分析...
下面進行測試,先請求該模塊實例,然後再該模塊實例的onNativeReady通道對象上註冊一個函數,函數內容就是打印一條信息。手動觸發這個通道事件,看註冊的函數是否執行。
//測試channel模塊
var channel = require("myphonegap/channel");
channel.onNativeReady.subscribe(function(){
console.info("onNativeReady");
}
);
console.info("before native ready");
channel.onNativeReady.fire();
console.info("after native ready");
測試結果::
;(function(){
var require,//myphonegap內部的工具函數,用來導入相關的模塊
define;//在myphonegap註冊相關的模塊
//通過一個立即調用的匿名函數,來給require和define賦上實際的函數
(function(){
var modules={}; // 模塊數組,添加模塊類似給這個對象添加了屬性,模塊名爲屬性名,模塊對象爲屬性值,或者說是鍵值對
build = function(module){ //根據模塊對象構造模塊導出對象,模塊導出對象存儲在modules這個對象數組內
var factory = module.factory;
module.exports = {}; //給當前模塊加入了一個exports屬性
delete module.factory; //刪除了module的屬性
factory(require,module.exports,module); //構建導出模塊,module.exports是傳出參數(實參,引用傳遞)
return module.exports;
}
require = function(id){ //根據模塊名稱/id請求模塊對象,如果是第一次請求,就構建對象
if(!modules[id]){
throw "module " + id + " not found!";
}
return modules[id].factory?build(modules[id]):modules[id].exports;
}
define = function(id,factory){ //定義模塊,模塊名稱、構建模塊對象的工廠方法。
if(modules[id]){
throw "module " + id + " is exist!";
}
modules[id] = { //定義模塊對象,左邊的值爲屬性名,右邊的值爲傳入的參數
id:id,
factory:factory
};
}
})();
//註冊myphonegap模塊
define("myphonegap", function(require, exports, module){
console.info("create myphonegap module");
var myphonegap = {
Hello:function(name){
console.info("hello, "+name +" !");
}
};
module.exports = myphonegap;
});
//註冊myphonegap/builder模塊
define("myphonegap/builder", function(require, exports, module) {
});
define("myphonegap/channel",function(require,exports,module){
var utils = require("myphonegap/utils"),
nextGuid = 1;
//典型的創建對象方法:通過構造器初始化變量,從而讓各個實例相互獨立;之後通過修改函數原型共享實例方法。
var Channel = function(type,sticky){
this.type = type;
//map of guid -> function
this.handlers = {};
// 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
this.state = sticky?1:0;
// Used in sticky mode to remember args passed to fire().
this.fireArgs = null;
// Used by onHasSubscribersChange to know if there are any listeners.
this.numHandlers = 0;
// Function that is called when the first listener is subscribed, or when
// the last listener is unsubscribed.
this.onHasSubscribersChange = null;
}, channel={
create:function(type){
channel[type] = new Channel(type,false);
},
createSticky:function(type){
channel[type] = new Channel(type,true);
},
deviceReadyChannelsArray: [],
deviceReadyChannelsMap: {},
waitForInitialization: function(feature) {
if (feature) {
var c = channel[feature] || this.createSticky(feature);
this.deviceReadyChannelsMap[feature] = c;
this.deviceReadyChannelsArray.push(c);
}
},
initializationComplete: function(feature) {
var c = this.deviceReadyChannelsMap[feature];
if (c) {
c.fire();
}
},
join: function (h, c) {//join也就是將一組Channel連接起來,並注入一個在所有Channel上只執行一次的公共處理函數
var i = c.length;
var len = i;
var f = function() {
if (!(--i)) h();
};
for (var j=0; j<len; j++) {
!c[j].fired?c[j].subscribe(f):i--;
}
if (!i) h();
}
};
function forceFunction(f){
if (f === null || f === undefined || typeof f != 'function') throw "Function required as first argument!";
}
//給對象Channel的原型對象添加訂閱函數,參數:函數對象、上下文、全局唯一ID
Channel.prototype.subscribe = function(f,c,g){
forceFunction(f);//確保f爲函數
if(this.state==2){ //apply方法能劫持另外一個對象的方法,繼承另外一個對象的屬性;f裏面的指針爲c(Channel的實例)
f.apply(c||this,this.fireArgs);
}
var func = f,
guid = f.observer_guid;
if(typeof f == "object"){ func = utils.close(c,f);}
if(!guid){
guid = ''+ nextGuid++;
}
f.observer_guid = guid;
func.observer_guid = guid;
//防止重複添加
if(!this.handlers[guid]){
this.handlers[guid] = func;
this.numHandlers++;
if(this.numHandlers===1){
this.onHasSubscribersChange&&this.onHasSubscribersChange();
}
}
};
Channel.prototype.unsubscribe = function(f){
forceFunction(f);
var guid = f.observer_guid;
if(this.handlers[guid]){
delete this.handlers[guid];
this.numHandlers--;
if(numHandlers===0){
this.onHasSubscribersChange&&this.onHasSubscribersChange();
}
}
};
//訂閱一次
Channel.prototype.subscribeOnce = function(f,c,g){
};
//調用了在通道訂閱的所有函數
Channel.prototype.fire = function(e){
//console.info('fire start:type/'+this.type + ' numHandlers/' +this.numHandlers);
var fail = false,
fireArgs = Array.prototype.slice.call(arguments);
// Apply stickiness.
if (this.state == 1) {
this.state = 2;
this.fireArgs = fireArgs;
}
if (this.numHandlers) {
// Copy the values first so that it is safe to modify it from within
// callbacks.
var toCall = [];
for (var item in this.handlers) {
toCall.push(this.handlers[item]);
}
for (var i = 0; i < toCall.length; ++i) {
toCall[i].apply(this, fireArgs);
// console.info(this.type+' enter func fire ');
}
if (this.state == 2 && this.numHandlers) {
this.numHandlers = 0;
this.handlers = {};
this.onHasSubscribersChange && this.onHasSubscribersChange();
}
}
};
channel.create('onDOMContentLoaded');
channel.create('onNativeReady');
channel.create('onDeviceReady');
channel.waitForInitialization('onMyphonegapReady');
channel.waitForInitialization('onMyphoneConnectionReady');
module.exports = channel;
//console.info('define myphonegap/channel completed');
});
//註冊myphonegap/common模塊
//配置對象,將公共模塊組織起來
define("myphonegap/common",function(require,exports,module){
});
define("myphonegap/exec", function(require, exports, module) {
});
//註冊myphonegap/platform模塊
define("myphonegap/platform", function(require, exports, module){
});
// 這裏省略了其它插件的註冊
//註冊myphonegap/utils模塊
define("myphonegap/utils", function(require, exports, module){
});
(function (context) {
}(window));
//所有模塊註冊完之後,再導入myphonegap至全局環境中
//window.myphonegap = require('myphonegap');
//window.myphonegap.Hello("wen");
//測試channel模塊
var channel = require("myphonegap/channel");
channel.onNativeReady.subscribe(function(){
console.info("onNativeReady");
}
);
console.info("before native ready");
channel.onNativeReady.fire();
console.info("after native ready");
})();