21

I want to be able to package my JavaScript code into a 'namespace' to prevent name clashes with other libraries. Since the declaration of a namespace should be a simple piece of code I don't want to depend on any external libraries to provide me with this functionality. I've found various pieces of advice on how to do this simply but none seem to be free of errors when run through JSLint (using 'The Good Parts' options).

As an example, I tried this from Advanced JavaScript (section Namespaces without YUI):

"use strict";
if (typeof(MyNamespace) === 'undefined') {
    MyNamespace = {};
}

Running this through JSLint gives the following errors:

Problem at line 2 character 12: 'MyNamespace' is not defined.
Problem at line 3 character 5: 'MyNamespace' is not defined.
Implied global: MyNamespace 2,3

The 'Implied global' error can be fixed by explicitly declaring MyNamespace...

"use strict";
if (typeof(MyNamespace) === 'undefined') {
    var MyNamespace = {};
}

...and the other two errors can be fixed by declaring the variable outside the if block.

"use strict";
var MyNamespace;
if (typeof(MyNamespace) === 'undefined') {
    MyNamespace = {};
}

So that works, but it seems to me that (since MyNamespace will always be undefined at the point it is checked?) it is equivalent to the much simpler:

"use strict";
var MyNamespace = {};

JSLint is content with this but I'm concerned that I've simplified the code to such an extent that it will no longer function correctly as a namespace. Is this final formulation sensible?

Matthew Murdoch
  • 30,874
  • 30
  • 96
  • 127
  • what particular concern do you have that `MyNamespace` will not function correctly as a namespace? – Russ Cam Feb 20 '10 at 11:05
  • Just a niggling feeling that since the advice out there suggests more complex formulations that I've missed some nuance which isn't obvious to me. – Matthew Murdoch Feb 20 '10 at 11:10

6 Answers6

19

Don't take JSLint's word as gospel. Much of what it says is sensible, but it also comes with a lot of Crockford's personal dogma attached. In particular I don't always agree with him about the best place for var.

"use strict";
if (typeof(MyNamespace) === 'undefined') {
    MyNamespace = {};
}

That one's no good; JSLint is correct to complain about the implied global. 'use strict' requires you don't imply globals.

"use strict";
if (typeof(MyNamespace) === 'undefined') {
    var MyNamespace = {};
}

That's fine. The var is hoisted so MyNamespace is present and set to undefined on entering the code block. So you could do that test as (MyNamespace===undefined) even without the typeof operator's magic ability to let you refer to variables that don't exist.

The other way is to use the unambiguous in operator (which is the only way to distinguish between an absent property one that is present but set to undefined). For the case of globals in a normal browser script, you can use it against the global window object:

'use strict';
if (!('MyNamespace' in window)) {
    window.MyNamespace = {};
}

(JSLint doesn't like that either, as ‘assume a browser’ doesn't seem to define window for some unfathomable reason. Hey ho.)

bobince
  • 528,062
  • 107
  • 651
  • 834
  • +1 - Nice answer! So does the formulation with `var MyNamespace;` outside of the if block (which gives no JSLint errors) have any semantic problems or is that also OK? – Matthew Murdoch Feb 20 '10 at 12:43
  • 2
    It's OK, as long as we are indeed talking global code. If you declare a global `var` that already has a value, nothing happens (it's not reset to `undefined`). Whereas if you declare a local `var` with the same name as a defined global, the local variable with value `undefined` hides the global. – bobince Feb 20 '10 at 17:41
7

You can try a much shorter version:

var MyNamespace = MyNamespace || {};

This should be valid in JSLint, even in strict mode, using the name if it already exists and creating it if it doesn't, as best a namespace in JavaScript can be expected to work.

Ryan
  • 1,372
  • 14
  • 20
  • 2
    This now fails, even when MyNamespace is added as a global predef. The error reported is 'MyNamespace' used out of scope. – Pappa Feb 21 '14 at 13:17
6

This is a REALLY old question but I thought I would answer anyway since none of the above cleared all errors of jslint for me because I suspect the lint'r has been updated :-)

There maybe other ways to do this but as of 2013 it's the best I can come up with

If you want an error free jsLint module pattern with a namespace and strict see below

Assuming that this is contained in some .js file ...

this.ns = this.ns || {}; // Check for global namespace and if not found create 

(function(ns) {
  'use strict'   // restrict usage to this module 

  ns.myFunction = function() {
  } 

} (this.ns)); // Pass in the global namespace you 'might' have created above and 
              // drop 'this' reference

'this' is necessary to avoid the out-of-scope error (seems like it shouldn't matter but I guess using 'this' is explicit vs. just using ns or var ns both of which toss errors.

The iffy pattern is required to avoid the global 'use strict' error

Of course other tools don't like the 'this' as an iffy param warning that it's global (which is the intent of course) so ... tomatoe tomato

George Stocker
  • 57,289
  • 29
  • 176
  • 237
Idistic
  • 6,281
  • 2
  • 28
  • 38
  • this is no longer "jslint compatible"! however, I fear there may be no way to do it, as explained by @bobince. :( – cregox Feb 25 '17 at 12:59
3

You can access global variables through the window["NAMEOFGLOBAL"] syntax, so you could do your check like this instead:

if(typeof(window['MyNamespace']) === 'undefined') {
Amber
  • 507,862
  • 82
  • 626
  • 550
  • 1
    I guess this assumes that the javascript code will be run as part of a web page (if the code is used within a firefox extension, for example, I don't think this will work). – Matthew Murdoch Feb 20 '10 at 11:54
  • Correct; `window` would generally be specific to browsers. – Amber Feb 20 '10 at 21:00
  • `this` normally refers to `window` in the global browser scope. Would `if(typeof(this['MyNamespace']) === 'undefined') {` work in places `window` could not be assumed? – dave1010 May 25 '10 at 12:39
1

Surely the point of the undefined check is to ensure that users are not double-loading or overwriting an existing namespace. As such, I think you've gone too far.

How about:

var MyNs;
if(MyNs==null){
    //foo()
}
spender
  • 117,338
  • 33
  • 229
  • 351
  • JSLint suggests using `===` rather than `==` (using 'The Good Parts' options) but with that and whitespace changes it is happy with this from a syntax point of view. – Matthew Murdoch Feb 20 '10 at 11:15
  • -1: checking against `null` is conceptually wrong and only works because of the evil-ness of `==` – Christoph Feb 20 '10 at 13:01
  • Christoph: But == null can identify nulls and undefined. +1 to the question –  May 05 '10 at 16:58
1

I've encountered this issue before. You definitely don't want to use your final form, as it is completely bypassing the check for a defined variable and will always overwrite it to an empty object, even if things were already stored in it.

I think your second to last form is the most appropriate for creating a namespace, but really the first form is decent as well. The errors that JSLint reports with it are sort of catch-22s in my opinion and not much to worry about. The reason I think it's not a big deal to leave off the declaration is that, in the case where the namespace is loaded at some previous point, you'd end up with JSLint's "variable was used before it was defined" error, so you're essentially just trading one warning for another.

Jimmy
  • 35,686
  • 13
  • 80
  • 98
  • Could you explain a bit more about why the errors are a 'catch-22'? – Matthew Murdoch Feb 20 '10 at 11:20
  • What benefit is the check for a defined variable giving - is it there just to prevent the 'double-loading' scenario highlighted by @spender? – Matthew Murdoch Feb 20 '10 at 11:22
  • Edited my answer to better explain why the warning JSLint gives you should be taken with a grain of salt. And yes, the point of the undefined check is to prevent accidentally overwriting a variable that's been used previously. In a simple application it may not ever come up, but when things start to get more complex and your code is spread across multiple files, it's a useful safeguard. – Jimmy Feb 20 '10 at 11:27
  • +1, but can JSLint pick up cases where the namespace is loaded twice(i.e. can it be used to review multiple .js files loaded together by an HTML file)? – Matthew Murdoch Feb 20 '10 at 11:37
  • To the best of my knowledge, you can run HTML through it, but it will just be looking at the code inside `script` tags and inline event handlers – it won't load external scripts and execute them in order in the way a browser does. – Jimmy Feb 20 '10 at 11:40