Namespaces in Javascript
-
In a ideal world, scripts are little code snippets that fulfill one purpose (and the design of HISE tries to enforce this paradigm as much as possible). However this isn't always the case, especially interface scripts tend to grow pretty quickly and reach a size where you have to think about organizing the structure of your code. A common solution for this task in other languages are namespaces which can be used to avoid naming conflicts and to group things that belong together.
But in Javascript there is nothing like a namespace concept. Instead you need to create a Object and fill its properties:
var MyStuff = {}; MyStuff.property1 = 2; MyStuff.function1 = function(){ return 2;}; Console.print(MyStuff.property1); // 2 Console.print(MyStuff.function1()); // 2
However this has some performance implications (every access needs to resolve the Object name first) as well as some limitations: almost every custom enhancement I added to the scripting engine of HISE like inline functions or
const
variables can't be members of objects and must be cranked into the global namespace.In my spirit of taking things from C++ that I like and port it to Javascript, I decided to add the namespace concept to the scripting engine. Huge parts of the parser needed to be rewritten so it's a bit experimental (there are a lot of edge case scenarios which had to be considered). But I think it's a valuable addition because you get a way of grouping functions and variables without any performance penalty like you do when using objects for it (the parser resolves the namespaces at compile time):
const var property1 = 5; namespace MyStuff { const var property1 = 2; inline function function1() { return property1; // return MyStuff.property1; (within namespaces you can either use the prefix or not) }; } namespace Other { const var property1 = Mystuff.function1(); // works between namespaces } MyFunctions.doSomething(); // 2
So far so good. However I tried to make the parser changes as uninvasive as possible and tried to avoid complicating the code, so there are some limitations of the namespace concept (compared to the C++ namespacing).
However they should not affect the behaviour when using namespaces as intended (they are just adding some weirdness when you go off road).Caveats
Leaking of global namespace
Every namespace is a superset of the global namespace. That means that you can access global variables through the namespace prefix even if it is not defined within the global namespace:
reg property1 = 5; namespace Empty { } Console.print(Empty.property1); // 5
This is a side effect of allowing the access to the variable within its namespace with the namespace prefix as well as without (the autocomplete will always spit out the full namespace so it would be pretty stupid to autocomplete faulty stuff).
No nesting of namespaces
You can't nest namespaces (define namespaces within other namespaces). One level of grouping should be enough even for complex scripts.
No standard
var
definitions within namespaceNamespaces currently work only for
const var
andreg
variables (you'll get another 32reg
slots with every namespace) as well asinline
functions. I didn't want to change the standard Javascriptvar
type because I can't forsee the side effects of this for the weirder use cases of Javascript. Like with every customization I add I try to stay compatible to the standard implementation, but to be honest with these two types you have pretty much any use case covered anyway - I actually never use the standardvar
type anymore.namespace Problem { var property1 = 5; } Console.print(Problem.property1); // 5 (is actual a side effect of the leaking described above) Console.print(property1); // 5 (should be undefined)
No namespace wrapping of
include()
A common practice in C++ is to wrap
#include
statements into a namespace definition (this way you don't need to define the namespace in every header file). However this isn't supported in HISE so this won't work:// External File (externalFile.js) const var property1 = 2; // Script namespace External { include("externalFile.js"); } Console.print(External.property1); // undefined
Of course you can use namespaces in external files (it would pretty much defeat the purpose of the whole thing if this wasn't possible).
No duplicated namespace usage
Every namespace must be defined once. In C++ you can take up existing namespaces and add your variables to it, but this isn't supported in HISE.
That's it. Feel free to play around with it (it will be included in Build 646) and write a comment if you find a bug or unexpected behaviour. If you really need to have one of the mentioned problems resolved, I could try to improve the parser to support it.
-
Has build 645 been updated with this feature?
-
Oops, my bad, I am talking about the upcoming 646 (I'll upload it in the next days)
-
I thought so but wanted to check before I re-downloaded the current build
-
Alright, Build 646 is online.
-
That was quick :D
-
Can
this
be used within a namespace to refer to its own functions? -
Nope,
this
is reserved for object properties. -
What about creating another keyword like
self
? -
Adding Python to the melting pot, I like it :)
Why do you need this? I would indeed prefer adding a new keyword like
self
, but I can't see any advantage yet. -
If we're at it, what do you think about a
private
keyword?namespace MyEncapsulatedStuff { private var internals = "Hidden"; inline function getHiddenProperty() { return self.internals; // :) }; }; Console.print(MyEncapsulatedStuff.internals); // undefined Console.print(MyEncapsulatedStuff.getHiddenProperty()); // "Hidden"
It would solve the problem of the weird Javascript encapsulation tactics.
-
A
self
keyword will be helpful for example when I have functions within a namespace that refer to each other and to private namespaced variables. Rather than writing out the namespace's name I could just putself
and if I decide to change the namespace name I won't need to adjust the function and variable references within the namespace. Theprivate
keyword is a good idea :) -
You could also skip the
namespace.
and write the variable name without prefix. This will work even if there are global variables with the same name.