JS应用在Firebug中的扩展架构模式(2)
下面这些代码是browserOverlay.js文件的内容,这个脚本文件会在一个界面文件(browserOverlay.xul)中被引用。
// 扩展对应的唯一全局变量
var myExtension = {};
(function() { // 注册命名空间
var namespaces = [];
this.ns = function(fn) {
var ns = {};
namespaces.push(fn, ns);
return ns;
};
// 初始化
this.initialize = function() {
for (var i = 0; i < namespaces.length; i += 2) {
var fn = namespaces[i];
var ns = namespaces[i + 1];
fn.apply(ns);
}
};
// 收尾的清理工作
this.shutdown = function() {
window.removeEventListener("load", myExtension.initialize, false);
window.removeEventListener("unload", myExtension.shutdown, false);
};
// 注册两个事件处理程序,维护扩展的生存期
window.addEventListener("load", myExtension.initialize, false);
window.addEventListener("unload", myExtension.shutdown, false);
}).apply(myExtension);
正如我前文所述,这里只有一个全局对象myExtension。
总结一下,这个对象要实现下面几个方法:
- ns - 注册一个新的命名空间。
- initialize - 初始化所有的命名空间。
- shutdown - 收尾的清理工作。
当然这段代码也会确保initialize和shutdown方法会在正确的时间被调用,这也是两个事件处理程序的作用。
browserOverlay.xul现在看起来可能会是下面这个样子:
<?xml version="1.0"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/
there.is.only.xul">
<script src="chrome://namespace/content/browserOverlay.js" type="application/x-javascript"/>
<script src="chrome://namespace/content/Module1.js" type="application/x-javascript"/>
<script src="chrome://namespace/content/Module2.js" type="application/x-javascript"/>
</overlay>
在这里,Module1.js和Module2.js两个文件是一模一样的。
myExtension.ns(function() {
// TODO: 脚本内的全部代码
});
在不同的模块间共享数据
我们已经把所有的脚本置于本地的作用域下,现在让我们来回答上面提到的第二个问题,就是在不同的命名空间下如何共享函数和数据。基本的思路当然是要利用我们唯一的全局对象啦,也就是myExtension。
首先,让我们先来看看下面这段代码(都在lib.js文件中)
myExtension.LIB = {
// 共享函数接口
getCurrentURI: function() {
return window.location.href;
},
// 扩展对象的快捷方式
theApp: myExtension,
// XPCOM组件的快捷方式
Cc: Components.classes,
Ci: Components.interfaces,
// 等等。。。
};
你可以注意到,这段代码在全局的myExtension对象下建立了一个新的LIB属性,这个属性定义了一个函数库,是要在扩展所有的模块中共享的。你应该在Java的包结构中看到过相同的做法,所有的命名空间呈树状结构分布在一个唯一的对象下面,YUI也是这样子做的。
lib.js文件也在browserOverlay.xul中引入,紧随browserOverlay.js的后面。
<?xml version="1.0"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/
there.is.only.xul">
<script src="chrome://myextension/content/browserOverlay.js" type="application/x-javascript"/>
<script src="chrome://myextension/content/lib.js" type="application/x-javascript"/>
<script src="chrome://myextension/content/Module1.js" type="application/x-javascript"/>
<script src="chrome://myextension/content/Module2.js" type="application/x-javascript"/>
</overlay>
让我们对模块内的脚本也做一些改进。
myExtension.ns(function() {
with(myExtension.LIB) {
// TODO: 脚本内的全部代码
var moduleVariable = "Accessible only from withing this module";
dump("myExtension.Module initialization " + getCurrentURI() + "\n");
}
});
通过利用with语句,我们可以方便的访问所有的库函数,就像访问全局变量一样。
既然我们要访问全局对象,还可以像下面这样利用theApp这个快捷方式(尤其是命名空间名字太长的时候)
myExtension.ns(function() {
with(myExtension.LIB) {
// TODO: 脚本内的全部代码
theApp.sharedValue = "A new shared property";
}
});
下面这个图是从UML的角度来纵观整个架构。
大家可以在 这里 下载本文提到的演示扩展。