就像@felix-kling 已经提到的那样,这是由于模块级别的隔离,如果您考虑一下,这很有意义。否则不仅Services
会被其他模块看到,而且abc
.
但是还有另一个重要的原因:由于 JS 代码模块被启动一次并在此之后被缓存,如果您导入two.jsm
两次,一次从一个已经导入的模块Services.jsm
,一次从另一个没有导入的模块,会发生什么?现在two.jsm
“看到”Services
将取决于首先导入哪个其他模块!这将是非常讨厌的。
在这种情况下,您对“abc 被导入 obj()”的评论是错误的。您的代码实际上导入abc
到顶级范围。Cu.import
将始终导入顶级范围,除非您明确指定要导入的另一个范围。
"abc" in this; // false
"abc" in obj; // false
obj.init();
"abc" in this; // true
"abc" in obj; // false!
如果您想导入two.jsm
,obj
则需要Cu.import
使用第二个参数进行调用。
let obj = {
init: function() {
Components.utils.import('chrome://myaddon/modules/two.jsm', this);
}
};
"abc" in this; // false
"abc" in obj; // false
obj.init();
"abc" in this; // false
"abc" in obj; // true
但这当然不会影响 的可见性Services
。
我想这会很有帮助,如果Cu.import
只是自动导入一些你无论如何都会导入的模块,例如Services.jsm
and XPCOMUtils.jsm
。但由于遗留原因和向后兼容性限制,这不会也可能永远不会发生。const {Promise} = Cu.import(..., {});
(例如,由于 ES6 添加了默认Promise
全局...;那种向后兼容性问题/约束,我有导入的代码中断)。
备择方案?
嗯,很明显的一个不是Cu.import
用于你自己的东西,而是使用其他东西。一堆附加组件,包括。当然,所有的 SDK 插件都有自己的 CommonJS 风格的require()
实现。
- 您实际上可以重复使用 SDK 加载程序,而不使用 SDK,或者如果您愿意,也可以只使用 SDK 的选定部分。请参阅
loader
文档。我知道 Erik 在其他非 SDK Scriptish插件中创建了一个加载器。
- 您可以基于下标加载器编写自己的自定义加载器,也许
Sandbox
. 例如,我在我的extSDK
样板文件中这样做了(loader.jsm 中的所有全局符号== loader.jsm::exports
对每个require
d 模块都是可见的)。
但是这样做可能需要相当多的额外工作、额外的知识和努力将现有的 JS 代码模块移植到require()
基于模块的模块上。