5

So I'm a fairly decent javascript programmer and I've just recently finished working on a fairly big web application that involved writing quite a bit of javascript. One of the things I can across when I was debugging my script was that there were some namespace conflicts with my various global variables I used throughout my script. Essentially, my javascript file was structured as such:

global var a
global var b
global var c
function1(){}
function2(){}
function3(){}

with a jQuery document on-ready function to bind various events to buttons in my html and call my functions as event handler callbacks.

Some people recommended encapsulating my entire script in one gigantic function to prevent any scope-related errors. I couldn't quite figure out exactly what that would entail. Any tips are appreciated as I am about to create another web app that will involve quite a bit of AJAX page loads to avoid browser refreshes and DOM manipulation bound to various events. Thanks!

Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
Casey Flynn
  • 13,654
  • 23
  • 103
  • 194

5 Answers5

3

I recommend reading the jQuery plugin authoring guide (I also recommend you consider using jQuery if you are not)

http://docs.jquery.com/Plugins/Authoring

BTW this been asked many times (not a criticism for re-asking)

I also highly recommend you read about jQuery live plugin for register DOM events(I guess its built-in now): http://api.jquery.com/live/ (this will minimize the nasty need for state management of unbinding and rebinding your DOM nodes).

Community
  • 1
  • 1
Adam Gent
  • 47,843
  • 23
  • 153
  • 203
  • yes I understand this type of question has probably been asked before but I was looking for general feedback concerning my design pattern of using global variables and simple functions defined in a file -- which I'm beginning to understand is somewhat bad. But thanks for the tip on the jQuery plugins guide I'm reading it now. And yes I have been using the jQuery framework. – Casey Flynn Jul 01 '11 at 03:16
  • Well you definitely need to wrap your code. If your question is design pattern/style related ie **procedural** (what you have now), **functional programming** (jquery), **oop** (mochikit, and prototype), then that is dependent on your style and framework choice. You should be able to all of the paradigms if you wrap the code (or not). – Adam Gent Jul 01 '11 at 03:20
  • @Casey Flynn Since you are using jQuery then I recommend following jQuery's style and best practices (plugin authoring guide). The procedural global variable practice is pretty eschewed by most. – Adam Gent Jul 01 '11 at 03:27
  • Gent, thanks for the tip with the .live() method. It looks very useful I'll definitely be using that in the future. I was previously just using .bind() – Casey Flynn Jul 01 '11 at 14:21
3

I like wrapping the contents of each file inside an anonymous function. You can then pass window to this as a parameter and selectively choose what to export from each file.

(function(exports) {

var MyClass = function() {

};

MyClass.prototype.method = function() {

};

// this won't be visible outside this file
var helperFunction = function() {

};

exports.module = exports.module || {};
exports.module.MyClass = MyClass;

})(window);

Also, you can structure it in the following way to use this as the global object instead, if that appeals more to your coding style:

(function() {

this.Thing = function() { };

}).call(window);
robbles
  • 2,729
  • 1
  • 23
  • 30
  • I like this pattern. I suggest a minor tweak: instead of setting `exports.module` equal to a new object, first check if it exists and if it does just add more methods to it; otherwise, create it as a new object. That way you can include similarly structured code in multiple source files and all will add to the same object (whichever is included first will create it). Methods will have access to other methods from the other files but you can also include methods/properties that are private to just one file (perfect for private helper functions or counters or whatever). – nnnnnn Jul 01 '11 at 03:47
  • @robbles & @nnnnnn, I also like this solution. Let me see if I understand this correctly. You wrap your code inside an anonymous function to prevent namespace conflicts and pass in the window object so you can assign any classes you create to a property of that window object to make it available elsewhere in your code? And @nnnnnn is suggesting that I check for the presence of .module as a property of the window object to make sure I don't just overwrite it if it already exists? – Casey Flynn Jul 01 '11 at 14:44
  • Also, someone mentioned to me that it's preferable to use the [''] syntax to access properties of objects rather than the . syntax for various reasons I don't feel like listing out. Do you guys agree? – Casey Flynn Jul 01 '11 at 14:45
  • Yes, that's the idea. You can either do like @nnnnnn suggests and extend a single global object, or you can just have one module per file. That's what I usually do. – robbles Jul 01 '11 at 18:55
  • I can't think of any reason to use [""] syntax over dot, except when looking up properties by names stored in strings. I know that some Javascript minifiers (like the one in Google Closure) recommend that you use dot syntax to avoid name optimizations breaking your code though. – robbles Jul 01 '11 at 19:00
3

A similar alternative to Michael's and nnnnnn's version is to do

var YourApp = {
    a: 1234,
    b: 5678,
    function1: function () {

    },
    etc
};

YourApp is the only global var and its properties can be accessed like

YourApp.function1();

or

YourApp.a;
shelman
  • 2,689
  • 15
  • 17
  • 1
    Yep, this is the next logical step after what I posted. Note (to @Casey) that having defined various member variables and methods up front like this you can still add more later the same way I did in my post. – nnnnnn Jul 01 '11 at 03:36
2

I expect to get downvoted from OO purists, but...

A very simple solution to the namespace collisions is to place your variables and functions into a class, even if it doesn't have a working constructor or perform any internal processing of its own.

function YourApp() {} // empty constructor...
YourApp.a = 1234;
YourApp.b = 5678;
YourApp.function1 = function() {};
YourApp.function2 = function() {};

function YourOtherApp() {} // empty constructor...
YourOtherApp.a = 1234;
YourOtherApp.b = 5678;
YourOtherApp.function1 = function() {};
YourOtherApp.function2 = function() {};

// Then you call it like:
YourApp.function1();

// And you have no more namespace collisions with other globals
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
  • This looks interesting, do I have to call the constructor of 'YourApp' before I can access 'YourApp.function1();' ? – Casey Flynn Jul 01 '11 at 03:14
  • @Casey Flynn no you don't have to call the constructor. These basically become static functions. If they were declared as `YourApp.prototype.function1 = function() {}` then they would operate on a class instance and require you to call the constructor first. – Michael Berkowski Jul 01 '11 at 03:15
  • @Casey Flynn Actually about that last part, I'm not sure right this minute if it would even be required to call the constructor of the prototype functions, but if there were internal properties they would be undefined without the constructor. – Michael Berkowski Jul 01 '11 at 03:16
  • If you're not going to call `YourApp` directly as a function and not going to use it as a constructor why declare it as a function at all? You might as well just say `var YourApp = {};`. – nnnnnn Jul 01 '11 at 03:26
  • @nnnnnn Yes that's true, and I do so on very small projects, but declaring the class adds future flexibility for when some internal work by the class becomes necessary. – Michael Berkowski Jul 01 '11 at 11:18
2

The quickest first step based on what you have done in the past with lots of global variables and functions is to simply take all of those and make them properties of a single object. That single object is declared as a global variable, but it is your only global variable and is effectively your new namespace and thus you only have to worry about one name potentially clashing with other libraries.

So relating that directly to the example you gave with a, b, etc:

var SNS = {}; // create some namespace object

SNS.a = "something";
SNS.b = "something else";
SNS.c = 17;
SNS.method1 = function(x) {
   alert(SNS.a + x);
};
SNS.method2 = function() {
   SSN.method1(12); // call another function
};

SNS.SUB = {};
SNS.SUB.property1 = "sub namespace prop 1";
SNS.SUB.method1 = function() {};
// etc.

My example uses 'SNS' for 'some namespace'; I'm sure you can immediately see how that would be pretty easy to apply to the project you just finished. You can probably also see the disadvantage that for your methods to refer to each other and to your variables you have to prefix them all with the name of your object. If you have sub namespaces that gets worse. Fortunately there are ways around that, but I'm declaring them outside the scope of this answer.

Having said all that, something for you to read up on (Google) is the "revealing module pattern" - will help you go a bit more OO (if that's what you want).

A really in-depth answer to your question can be found here: http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/

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

nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • thanks for the reading suggestions I'll definitely be reading those since I anticipate working more and more with javascript in the near future – Casey Flynn Jul 01 '11 at 14:25