It is difficult to work with independently developed JavaScripts. There is no built-in Java "package" (or C++ "namespace")-like feature in JavaScript. You have to use script objects for everything. Yes, there is a namespace proposal in JavaScript 2.0. But, what do we do now?
Well designed JavaScript APIs (like TrimPath API) avoid polluting global namespace by exposing API as methods in few (may be just one!) objects. But, most of the useful JavaScript code out there may not have been written in that style. There will be many global variables and functions. You may still find those scripts useful. It is not always feasible or desirable to edit such scripts to avoid global namespace pollution. How about a simple namespace solution for JavaScript (in the context of Mustang's JavaScript engine)?
function namespace(code) {
// new global scope for the new namespace
var myGlobals = new Packages.javax.script.SimpleBindings();
// use 'engine' variable to execute code with
// new global scope
// 'engine' variable is of type javax.script.ScriptEngine
// This is exposed in jrunscript tool. But, it is easy to expose this
// from your Java programs as well. Please refer below...
engine.eval(code, myGlobals);
// return a wrapper object that exposes
// globals of the new namespace
return new JSAdapter() {
__has__: function (name) {
return myGlobals.containsKey(name);
},
__get__: function (name) {
return myGlobals.get(name);
},
__getIds__ : function () {
return myGlobals.keySet().toArray();
}
}
}
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine engine = m.getEngineByName("js");
// add a global variable called "engine"
engine.put("engine", engine);
With the above definition of namespace (I'd have preferred 'package', but that
is a JavaScript reserved word!), we can write:
var ns = namespace('var s = 3; function hello() { print('hello'); }');
// print 3 - value from namesapce
print(ns.s);
// print 'hello' by calling hello in namespace ns
ns.hello();
For better package/namespace level encapsulation, we may want to expose only functions from the namespace and hide all package-level variables. It is easy to modify above namespace function as shown below:
function namespace(code) {
var myGlobals = new Packages.javax.script.SimpleBindings();
engine.eval(code, myGlobals);
return new JSAdapter() {
__has__: function (name) {
// do not expose non-function type stuff!
var res = myGlobals.containsKey(name);
if (res) {
return typeof(myGlobals.get(name)) == 'function';
}
},
__get__: function (name) {
// do not expose non-function type stuff!
var val = myGlobals.get(name);
return typeof(val) == 'function'? val : undefined;
},
__getIds__ : function () {
return myGlobals.keySet().toArray();
}
}
}
Now, we have namespace. What about namespace using or package import-like feature? Well, that is a topic for another blog or an exercise to reader :-)