Namespaces in Javascript


  • administrators

    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 namespace

    Namespaces currently work only for const var and reg variables (you'll get another 32 reg slots with every namespace) as well as inline functions. I didn't want to change the standard Javascript var 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 standard var 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?


  • administrators

    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


  • administrators

    Alright, Build 646 is online.



  • That was quick :D



  • Can this be used within a namespace to refer to its own functions?


  • administrators

    Nope, this is reserved for object properties.



  • What about creating another keyword like self?


  • administrators

    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.


  • administrators

    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 put self and if I decide to change the namespace name I won't need to adjust the function and variable references within the namespace. The private keyword is a good idea :)


  • administrators

    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.


Log in to reply

Looks like your connection to Namespaces in Javascript was lost, please wait while we try to reconnect.