Reputation: 7721
For clarification, please consider the following simplified example:
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
}
two.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
}
I know that this is how it works but the question is why?
Result is that for example Services.jsm
has to be imparted in every module.
Although Firefox caches the modules and there isn't much of a performance difference, I would like to know if the repeated importation can be avoided?
Upvotes: 0
Views: 77
Reputation: 33162
Like @felix-kling already mentioned, that's due to module level isolation and it makes a lot of sense if you think about it. If it was otherwise not only Services
would be seen by other modules, but also abc
.
There is another important reason, though:
Since JS code modules are initiated once and cached after that, what would happen if you imported two.jsm
twice, once from a module already having imported Services.jsm
and once from another module not having done so? Now two.jsm
"seeing" Services
would depend on which of the other modules was imported first! Which would be extremely nasty.
In that context, your comment about "abc is imported into obj()" is wrong. Your code actually imports abc
into the top-level scope. Cu.import
will always import into the top-level scope, unless you explicitly specify another scope to import to.
"abc" in this; // false
"abc" in obj; // false
obj.init();
"abc" in this; // true
"abc" in obj; // false!
If you wanted to import two.jsm
into obj
, you'd need to call Cu.import
with a second argument.
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
But that does not affect the visibility of Services
, of course.
It would be helpful I guess, if Cu.import
just auto-imported some modules you'd import anyway, such as Services.jsm
and XPCOMUtils.jsm
. But this does not and likely will not ever happen due to legacy reasons and backward-compatibility constraints. (E.g. I had code break that imported const {Promise} = Cu.import(..., {});
because ES6 added a default Promise
global...; that kind of backward-compatibility issues/constraints).
Well, the obvious one is not to use Cu.import
for your own stuff, but use something else. A bunch of add-ons, incl. all SDK add-ons of course, have their own CommonJS-style require()
implementation.
loader
documentation. I know that Erik creates a loader in the otherwse non-SDK Scriptish add-on.Sandbox
. E.g. I did so in my extSDK
boilerplate (all global symbols in loader.jsm == loader.jsm::exports
will be visible to each require
d module).But doing so may require a quite bit of extra work, extra knowledge, and effort to port existing JS code modules to require()
based modules.
Upvotes: 2