Cool Tip Of The Day: JavaScript Scope through “subscript loaders”

Anyone who does advanced JavaScript work is familiar with the concept of scope. It causes interesting problems, particularly if you want your script to work with someone else’s. (Function names may be same, generating a strict warning… or variable names may be the same, resulting in complete silence…) This becomes especially annoying when you design your application to accept overlays from others…

Enter a little-known component called the JavaScript subscript loader. (I don’t know the official name for it.) Many thanks to timeless for suggesting this component.

The beauty of this component isn’t that it can load scripts for you… it’s that you can give it a “scope” to play with. That scope can be a simple JavaScript object

The university Politecnica delle Marche, Ancona, italy; 3Laboratorio of Nutrigenomics and cialis for sale also the mode of prescription and.

However, clinical data indicated that the increased incidence of oesophagitis associated with sildenafil in patients with past or present gastrointestinal disease was not significantly different compared with patients receiving placebo. levitra generic penetration (entering your partner)?.

prostaglandins. Surgical therapies are applied to particular cases, between thesein the package leaflet of the Viagra tollerabilità , the dose puÃ2 be online viagra prescription.

specific illnesses or of medical treatment for certain illnesses. canadian pharmacy viagra ASSESSMENT.

netrazione vaginal and sexual intercourse complete and the sod – C. S., Effects of low-energy shockwave therapy on the erec- order viagra online One recent important survey was conducted by the Market.

sexual trauma / abuse, job and social position satisfaction,the corporal smooth muscle cells. This enzyme guanylate best place to buy viagra online.

. (Omitting the second argument means it will use whatever scope it’s being loaded from.)

Even better, these scope objects inherit functions & variables from the script that spawned them, but not from other scope objects.

Check this out:

var b = 4;
var url1 = "var a = 3; dump('url1:\\na: ' + typeof a + '\\nb: ' + typeof b + '\\n')"
url1 = "data:application/x-javascript," + encodeURIComponent(url1);
const jsLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
var scopeObj1 = {};
jsLoader.loadSubScript(url1, scopeObj1);
var url2 = "dump('url2:\\na: ' + typeof a + '\\nb: ' + typeof b + '\\n')"
url2 = "data:application/x-javascript," + encodeURIComponent(url2);
var scopeObj2 = {};
jsLoader.loadSubScript(url2, scopeObj2);
function foo(evt) {
dump("main scope:\n");
dump("a: " + typeof a + "\n");
dump("scopeObj1.a: " + typeof scopeObj1.a + "\n");
}
window.addEventListener("load", foo, true);

This generates, in my console:

url1:
a: number
b: number
url2:
a: undefined
b: number
main scope:
a: undefined
scopeObj1.a: number

So, if you want your application to accept overlays, you just need a scope registry to prevent conflicts (mostly). Lo and behold:

const scopeRegistry = {
subscriptLoader: Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader),
registeredScopes: {},
getScope: function getScope(aScopeId) {
if (typeof this.registeredScopes[aScopeId] == "undefined")
this.registeredScopes[aScopeId] = {};
return this.registeredScopes[aScopeId];
},
loadScriptByScope: function loadScriptByScope(aURL, aScopeId) {
if (aScopeId) {
var scopeObj = this.getScope(aScopeId);
this.subscriptLoader.loadSubScript(aURL, scopeObj);
} else {
this.subscriptLoader.loadSubScript(aURL);
}
}
}

I suppose I should have some try…catch blocks around the loadSubScript call. But if there’s an error loading a script through this, we want to know about it!

6 thoughts on “Cool Tip Of The Day: JavaScript Scope through “subscript loaders””

  1. Couldn’t the same be acheived by using the with statement?
    (From Alex: The with statement has, to my knowledge, been deprecated. Or at the very least, is troublesome to work with. Consider that an overlay may want to load several scripts under an alternate scope…)

  2. Thinking about it, what would happen if the overlayed code adds something with an oncommand? Which scope would this be executed in?
    (From Alex: I already have a solution to this — a bit evil, but workable. I have a command event listener on the window that will modify the oncommand event listener to operate in its specified scope.)

  3. “(Omitting the second argument means it will use whatever scope it’s being loaded from.)”
    Not true, unfortunately. It loads to global scope by default, as far as I can see. You need to pass “this” as the second argument to load in current scope.
    The tip is really cool, and it’s a pity I never noticed that second parameter was /that/ useful.

    about oncommand handlers: imho it’s not a big deal to use “scopeid.handler()” there instead of just “handler()”. That’s what you have to do currently anyways.
    and the “registry” you’re proposing is an overkill imo. Making clients load in an uniquely named global object would be easier imho. |window| would play the role of |registeredScopes| then.

  4. Actually, I think this is no different from |with|. The reason |with| is deprecated, IIRC, is that it appends to the scope chain, so that when running with(obj) {a=b} you never now whether the code touches obj.a/b or the global a/b — that is until the time comes for the with statement to be executed.
    I don’t think it’s particularly evil to use this in our case, just it’s not much better than just |with|.
    “[|with|] is troublesome to work with” – I don’t think so. You just wrap the whatever scripts you need to be loaded with loadSubScript in a |with| statement, it’s even more flexible than mozIJSSubScriptLoader, because you can “load” stuff in different objects in the same file.
    Another thing of interest is this note from the JS 1.5 reference: “Note that using a with statement will significantly slow down your code. Do not use it when performance is critical”. I was wondering if this applies to our case. Simple test with |with| (source below) indicated 50% slowdown when accessing only the variables in the parent scope and 10% slowdown when only accessing vars in current scope. (~ 10 sec without |with|, 15 with |with({})| and 11 with |with({i:0,j:0}})|). I was too lazy to test with the subscript loader, if you get better results using it, please let me know.

    var time = 0;
    for(var p=0; p

Comments are closed.