15

I want to create a global namespace for my application and in that namespace I want other namespaces:

E.g.

Dashboard.Ajax.Post()

Dashboard.RetrieveContent.RefreshSalespersonPerformanceContent();

I also want to place them in seperate files:

  • Ajax.js
  • RetrieveContent.js

However I have tried using this method, however it won't work because the same variable name is being used for the namespace in 2 seperate places. Can anyone offer an alternative?

Thanks.

Gert Grenander
  • 16,866
  • 6
  • 40
  • 43
Jamie C
  • 151
  • 1
  • 3

11 Answers11

9

You just need to make sure that you don't stomp on your namespace object if it's already been created. Something like this would work:

(function() {
    // private vars can go in here


    Dashboard = Dashboard || {};
    Dashboard.Ajax = {
        Post: function() {
            ...
        }
    };
})();

And the RetrieveContent file would be defined similarly.

Gabe Moothart
  • 31,211
  • 14
  • 77
  • 99
  • Hi, I am trying to put my code in namespaces too, but I have one question: I have a lot of JavaScript files, should I then add `Dashboard = Dashboard || {};` (if that would be my namespace) at every file? And second, do I have to prefix every variable to Dashboard.x in stead of x or is that not necessary because everything lives in the same namespace? – Michel Apr 06 '16 at 15:13
7

Here is a very good article on various "Module Patterns" in JavaScript. There is a very nice little section on how you can augment modules, or namespaces and maintain a cross-file private state. That is to say, the code in separate files will be executed sequentially and properly augment the namespace after it is executed.

I have not explored this technique thoroughly so no promises... but here is the basic idea.

dashboard.js

(function(window){

    var dashboard  = (function () { 
        var my = {}, 
            privateVariable = 1; 

        function privateMethod() { 
            // ... 
        } 

        my.moduleProperty = 1; 
        my.moduleMethod = function () { 
            // ... 
        }; 

        return my; 
    }());

    window.Dashboard = dashboard;
})(window);

dashboard.ajax.js

var dashboard = (function (my) { 
    var _private = my._private = my._private || {}, 
        _seal = my._seal = my._seal || function () { 
            delete my._private; 
            delete my._seal; 
            delete my._unseal; 
        }, 
        _unseal = my._unseal = my._unseal || function () { 
            my._private = _private; 
            my._seal = _seal; 
            my._unseal = _unseal; 
        }; 

    // permanent access to _private, _seal, and _unseal

    my.ajax = function(){ 
        // ...
    }

    return my; 
}(dashboard || {}));

dashboard.retrieveContent.js

var dashboard = (function (my) { 
    var _private = my._private = my._private || {}, 
        _seal = my._seal = my._seal || function () { 
            delete my._private; 
            delete my._seal; 
            delete my._unseal; 
        }, 
        _unseal = my._unseal = my._unseal || function () { 
            my._private = _private; 
            my._seal = _seal; 
            my._unseal = _unseal; 
        }; 

    // permanent access to _private, _seal, and _unseal

    my.retrieveContent = function(){ 
        // ...
    }

    return my; 
}(dashboard || {}));
Derek Adair
  • 21,846
  • 31
  • 97
  • 134
  • Could you please explain to me how does this allow accessing private variables that were declared in another file? More specified if I were to call dashboard._seal(), how does dashboard._unseal() allow me to access the private again? – Parth Shah May 30 '15 at 08:55
4

The Yahoo Namespace function is exactly designed for this problem.

Added:

The source of the function is available. You can copy it into your own code if you want, change the root from YAHOO to something else, etc.

Larry K
  • 47,808
  • 15
  • 87
  • 140
3

There are several libraries that already offer this sort of functionality if you want to use or examine a pre-baked (that is, a tested) solution.

The simplest and most bug free one to get going with is probably jQuery.extend, with the deep argument set to true. (The reason I say it is bug free is not because I think that jQuery.extend suffers from less bugs than any of the other libraries -- but because it offers a clear option to deep copy attributes from the sender to the receiver -- which most of the other libraries explicitly do not provide. This will prevent many hard-to-diagnose bugs from cropping up in your program later because you used a shallow-copy extend and now have functions executing in contexts you weren't expecting them to be executing in. (If however you are cognizant of how you will be extending your base library while designing your methods, this should not be a problem.)

Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
3

With the NS object created, you should just be able to add to it from where ever. Although you may want to try var NS = NS || {}; to ensure the NS object exists and isn't overwritten.

// NS is a global variable for a namespace for the app's code
var NS = NS || {};

NS.Obj = (function() {

  // Private vars and methods always available to returned object via closure
  var foo; // ...

  // Methods in here are public
  return {
    method: function() {

    }
  };

}());
dylanfm
  • 6,292
  • 5
  • 28
  • 29
1

i wrote this function to simplify creating namespaces. Mabey it will help you.

function ns(nsstr) {
    var t = nsstr.split('.');
    var obj = window[t[0]] = window[t[0]] || {};
    for (var i = 1; i < t.length; i++) {
        obj[t[i]] = obj[t[i]] || {};
        obj = obj[t[i]];
    }
}

ns('mynamespace.isawesome.andgreat.andstuff');
mynamespace.isawesome.andgreat.andstuff = 3;

console.log(mynamespace.isawesome.andgreat.andstuff);
dss
  • 470
  • 5
  • 12
1

You could do something like this...

HTML page using namespaced library:

<html>
<head>
    <title>javascript namespacing</title>
    <script src="dashboard.js" type="text/javascript"></script>
    <script src="ajax.js" type="text/javascript"></script>
    <script src="retrieve_content.js" type="text/javascript"></script>
    <script type="text/javascript">
        alert(Dashboard.Ajax.Post());
        alert(Dashboard.RetrieveContent.RefreshSalespersonPerformanceContent());
        Dashboard.RetrieveContent.Settings.Timeout = 1500;
        alert(Dashboard.RetrieveContent.Settings.Timeout);
    </script>
</head>

<body>
    whatever...
</body>

</html>

Dashboard.js:

(function(window, undefined){
    var dashboard = {};
    window.Dashboard = dashboard;
})(window);

Ajax.js:

(function(){
    var ajax = {};
    ajax.Post = function() { return "Posted!" };
    window.Dashboard.Ajax = ajax
})();

Retrieve_Content.js:

(function(){
    var retrieveContent = {};
    retrieveContent.RefreshSalespersonPerformanceContent = function() { 
        return "content retrieved"
    };


    var _contentType;
    var _timeout;
    retrieveContent.Settings = {
        "ContentType": function(contentType) { _contentType = contentType; },
        "ContentType": function() { return _contentType; },
        "Timeout": function(timeout) { _timeout = timeout; },
        "Timeout": function() { return _timeout; }
    };

    window.Dashboard.RetrieveContent = retrieveContent;

})();

The Dashboard.js acts as the starting point for all namespaces under it. The rest are defined in their respective files. In the Retrieve_Content.js, I added some extra properties in there under Settings to give an idea of how to do that, if needed.

ironsam
  • 630
  • 5
  • 15
  • There's no guarantee though that `retrieve_content.js` will be loaded and parsed after `Dashboard.js`. If any of the dependent libraries are loaded before `Dashboard.js` is loaded then the assignments will fail. – Sean Vieira Aug 05 '10 at 04:51
  • 1
    In general `Dashboard.js` will be loaded and parsed first, but yes, that isn't guaranteed. The `Dashboard` object could be checked before assignment and created if necessesary, but that would require some duplicated code in `retrieve_content.js` and `ajax.js`. The separate files requirement of the OP led me to the above. – ironsam Aug 05 '10 at 05:31
1

I believe the module pattern might be right up your alley. Here's a good article regarding different module patterns.

http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth

Cristian Sanchez
  • 31,171
  • 11
  • 57
  • 63
  • Answers with just links are not that useful. There should be enough of an explanation here. The question is how to define objects in namespaces from two different locations – Ruan Mendes Feb 13 '12 at 18:10
  • 1
    @JuanMendes: The article explains exactly that. Several of the top answers are basically just links as well. Besides, this was more than a year and a half ago. – Cristian Sanchez Feb 13 '12 at 19:17
  • I never really found the module pattern to be appropriate for namespacing. A namespace isn't a block of "reusable" code (though individual pieces of the NS may be). Object literals have always worked well enough for me. – 1nfiniti Jul 29 '13 at 20:55
1

I highly recommend you use this technique:

https://github.com/mckoss/namespace

  namespace.lookup('com.mydomain.mymodule').define(function (ns) {
  var external = namespace.lookup('com.domain.external-module');

  function myFunction() {
    ...
  }

  ...

  ns.extend({
    'myFunction': myFunction,
     ...
  });
});

I've been using this pattern for a couple of years; I wish more libraries would do the same thing; it's made it much easier for me to share code across my different projects as well.

mckoss
  • 6,764
  • 6
  • 33
  • 31
0

bob.js can help in defining your namespaces (among others):

bob.ns.setNs('Dashboard.Ajax', {

    Post: function () { /*...*/ }
});

bob.ns.setNs('Dashboard.RetrieveContent', {

    RefreshSalespersonPerformanceContent: function () { /*...*/ }
});
Tengiz
  • 8,011
  • 30
  • 39
0

Implementation:

namespace = function(packageName)
{
    // Local variables.
    var layers, layer, currentLayer, i;

    // Split the given string into an array.
    // Each element represents a namespace layer.
    layers = packageName.split('.');

    // If the top layer does not exist in the global namespace.
    if (eval("typeof " + layers[0]) === 'undefined')
    {
        // Define the top layer in the global namesapce.
        eval(layers[0] + " = {};");
    }

    // Assign the top layer to 'currentLayer'.
    eval("currentLayer = " + layers[0] + ";");

    for (i = 1; i < layers.length; ++i)
    {
        // A layer name.
        layer = layers[i];

        // If the layer does not exist under the current layer.
        if (!(layer in currentLayer))
        {
            // Add the layer under the current layer.
            currentLayer[layer] = {};
        }

        // Down to the next layer.
        currentLayer = currentLayer[layer];
    }

    // Return the hash object that represents the last layer.
    return currentLayer;
};


Result:

namespace('Dashboard.Ajax').Post = function() {
    ......
};

namespace('Dashboard.RetrieveContent').RefreshSalespersonPerformanceContent = function() {
    ......
};


Gist:

namespace.js

Takahiko Kawasaki
  • 18,118
  • 9
  • 62
  • 105