0

为澄清起见,请考虑以下简化示例:

one.js

Components.utils.import('resource://gre/modules/Services.jsm');

let obj = {

  init: function() {

   Components.utils.import('chrome://myaddon/modules/two.jsm', this);
  }

  // code here has access to Services.jsm
}

二.js

this.EXPORTED_SYMBOLS = ['abc'];

this.abc = {

  // abc is imported into obj()
  // however as part of obj (), abc{} does not have access to Services.jsm
}

我知道这就是它的工作原理,但问题是为什么?
结果是例如Services.jsm必须在每个模块中传授。
虽然火狐缓存了模块,性能差别不大,但我想知道是否可以避免重复导入?

4

1 回答 1

2

就像@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.jsmobj则需要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.jsmand 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对每个required 模块都是可见的)。

但是这样做可能需要相当多的额外工作、额外的知识和努力将现有的 JS 代码模块移植到require()基于模块的模块上。

于 2014-07-14T18:11:53.220 回答