JavaScript code modules 在Gecko1.9中引入并被用于具备不同权限的作用域之间的代码共享。 Modules 也可以用于创建全局js单例,这在之前是通过使用XPCOM(跨平台组件模型)实现的。一个 JavaScript code module 是仅仅是位于注册位置的一些 JavaScript 代码。Module 被载入特定的js域,比如 XUL script or JavaScript XPCOM script, 通过Components.utils.import()
or Components.utils["import"]()
.
创建 JavaScript code module
一个简单的例子my_module.jsm:
var EXPORTED_SYMBOLS = ["foo", "bar"]; function foo() { return "foo"; } var bar = { name : "bar", size : 3 }; var dummy = "dummy";
可以看到 module 使用规范的js语言来创建函数,对象,常量以及其他的数据类型。 Module 还定义了一个名为 EXPORTED_SYMBOLS 的特殊数组,这个数组中的每一项都会被输出到声明中并且被注入到引入的作用域中。例如:
Components.utils.import("resource://app/my_module.jsm"); alert(foo()); // displays "foo" alert(bar.size + 3); // displays "6" alert(dummy); // displays "dummy is not defined" because 'dummy' was not exported from the module
The URL for a code module
正如你在上例中看到的, 你需要一个 URL 来导入一个代码块 (code module)。 (就像例子中的 "resource://app/my_module.jsm".)
有三种模式的URL可供选择: chrome:……(), resource:……, 或者 file:…….
- 如果你在为 Firefox 4 写一个扩展并且已经有了一个 chrome.manifest(里面是内容指示), 你可以将代码块 code module 放入你的内容文件夹并通过
chrome://<yourextension>/content/<yourmodule>.jsm指向它。
- 如果你的扩展或应用需要支持 Mozilla 1.9.x (Firefox 3.x), 你需要注册一个新的资源URL. 具体细节在这: "Extending resource: URLs" section .
通过code modules共享对象
Components.utils.import()
的一个非常重要行为就是 module 加载后会被缓存起来, 并且随后的的导入不会重新加载一个新版的module, 而是使用之前缓存的版本。这就意味着当导入 module 多次时, 一个给定的 module 会被共享多次。只要引入某个module,任何对数据,对象和函数的变化都会体现在不同的引入该module的域中。比如,一个简单的module被引入到两个不同的域中,在其中一个域中做改变,另一个域也共享这种改变。如下例 :
Scope 1:
Components.utils.import("resource://app/my_module.jsm"); alert(bar.size + 3); // displays "6" bar.size = 10;
Scope 2:
Components.utils.import("resource://app/my_module.jsm"); alert(foo()); // displays "foo" alert(bar.size + 3); // displays "13"
这种共享行为可以被用来创建单例对象(singleton objects)并且可以被 跨windows 和 XUL script 和 XPCOM 组件所共享。
注意:引入某个module的域获得了一组该module中定义的输出符号的按值复制的副本( by-value copy )。 对这些输出符号的改变或者重新定义不会传播到其他的域中。 (虽然对象的属性会通过引用来改变。)。例子如下:
Scope 1:
Components.utils.import("resource://app/my_module.jsm"); bar = "foo"; alert(bar); // displays "foo"
Scope 2:
Components.utils.import("resource://app/my_module.jsm"); alert(bar); // displays "[object Object]"
按值复制的主要效果就是简单类型的全局变量不会在不同的域之间共享。所以我们经常将变量放在大括号中,然后以对象的形式输出 (就像上例中的bar)。
Unloading code modules
(Firefox 7.0 / Thunderbird 7.0 / SeaMonkey 2.4)Components.utils.unload()
允许你卸载之前引入的module。该方法被调用后, 指向该module的引用可以继续工作,但是后续对该module的引入,就会重新加载并重新分配指向该module的引用值。
Extending resource: URLs
Gecko 2.0之前,加载code module最常用的方式是—— resource: URLs. Resource URL 的基本语法如下:
resource://<alias>/<relative-path>/<file.js|jsm>
<alias>
是某个位置的别称,通常是一个相对于应用程序或者 XUL runtime的物理位置。这是一些由XUL runtime设置的预定义的别称:
app
- XUL application 位置的别名gre
- XUL runtime 位置的别名
<relative-path>
可以是多层的,经常是相对于alias的位置。最常见的相对路径是 "modules" 并常常被XUL Runner and Firefox使用. Code modules 是简单的js文件以 .js or .jsm 作为后缀。
<alias>
对于你的插件必须是唯一的,因为应用和其他的扩展共享同样的命名空间对所有的别称。
Using chrome.manifest
The easiest way for extensions and XUL applications to add custom aliases is by registering an alias in the chrome manifest using a line like this:
resource aliasname uri/to/files/
For example, if the XPI for your foo extension includes a top-level modules/ directory containing the bar.js module (that is, the modules/ directory is a sibling to chrome.manifest and install.rdf), you could create an alias to that directory via the instruction:
resource foo modules/
(Don't forget the trailing slash!) You could then import the module into your JavaScript code via the statement:
Components.utils.import("resource://foo/bar.js");
Programmatically adding aliases
Custom aliases to paths that can be represented as an nsILocalFile
can be programmatically added as well. For example:
// Import Services.jsm unless in a scope where it's already been imported Components.utils.import("resource://gre/Services.jsm"); var resProt = Services.io.getProtocolHandler("resource") .QueryInterface(Components.interfaces.nsIResProtocolHandler); var aliasFile = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); aliasFile.initWithPath("/some/absolute/path"); var aliasURI = Services.io.newFileURI(aliasFile); resProt.setSubstitution("myalias", aliasURI); // assuming the code modules are in the alias folder itself
Notes
自定义 modules 和 XPCOM components
Note that prior to Gecko 2.0 JavaScript XPCOM components are loaded before chrome registration. This means you can't use Components.utils.import()
with your own resource URL at the top level in a component source. A possible solution is moving the call to Components.utils.import()
into the XPCOM component constructor (discussion).
Packaging notes
It's important to note that you should not typically put your JavaScript code modules in a JAR file in your add-on. Firefox 3.6 doesn't support them at all, and there's only one case in which it's remotely useful: a Firefox 4-only add-on which must be installed unpacked. Otherwise placing code modules in a JAR file breaks compatibility unnecessarily.
See also
JavaScript code modules topic page.G