Andrew Latham
Andrew Latham

Reputation: 6132

Javascript namespacing only works with var

I have the following bit of code:

(function(){
...
var n = n || {};
...
})(); 

This does what I want, setting n equal to {} since this is the first time it's encountered and will be undefined. Unfortunately, since this inside a function, n is limited in scope to the function and I can't use it in other scripts.

I wanted to just change the line to n = n || {}, but I got an error: ReferenceError: n is not defined

Changing it to just n = {} worked as expected; however, this is not what I want. I don't get why n's undefinedness causes an error when I don't use var and works as expected (being a falsey value used to get the right side of the OR statement) when I do. Based on my understanding of the var keyword, I would expect it to be both or neither.

Why does the var matter, what is going on here?

Upvotes: 0

Views: 107

Answers (9)

LetterEh
LetterEh

Reputation: 26696

1) in the first round of evaluation, var n = n || {}; you're creating the variable:

var n = undefined;

2) in the second round of evaluation, you are setting the value conditionally:

n = undefined || {};

3) without the var statement, you've got a var which doesn't exist anywhere at all, being referenced

var myVar = non_existant_var; // error

If the var already exists, say, in the global scope, or any other function which the current function is nested inside, it will use whatever n it finds, on the way up the chain. If none exists, it will have a fit.

So rather than intentionally poking holes in your code, trying to get the code to know which scope you're trying to access, why not access it directly?

(function (name) {
    window[name] = window[name] || {}; // returns undefined and not reference error
    var ns = window[name];

    ns.init = function () {};
    // .............
    /* Don't return the application to a var -- it already exists in the global scope */
}("BigLongApplicationName"));

BigLongApplicationName.init(); // === window.BigLongApplicationName.init();

Upvotes: 0

Pete
Pete

Reputation: 2558

So it sounds like you're having trouble creating a global variable.

Good.

Creating global variables in JavaScript is easier than it should be.

Your problem is that n is not defined (duh). You can't reference a variable that's not yet defined. But you're probably thinking, "How is that different from the version using var???" And that's a good question.

JavaScript does something called "hoisting" of variable definitions. If you define a variable within a function, that definition gets implicitly relocated to the top of the function. So while you wrote:

var n = n || {};

What really happened was closer to:

var n;
n = n || {};

However, it's not really that simple, because if you tried to write that code, you'd wind up with n always being set to {}. But the general essence is there. The variable declaration happens before the assignment.

If you remove the var, it will cause a reference error because there's no declaration to hoist anymore. So one "proper" (and I use that term loosely, because it's never "proper" to create a global variable) way to do what you want is to take your var and place it outside your function wrapper. Like so:

var n = n || {};
(function () {
    //do stuff.
}()); 

Unfortunately, you can't do it like this:

var n; 
(function () {
    n = n || {};
}());

That has the same "problem" as my example above. If n is defined elsewhere, it will be set to undefined by the var n; and then set to {} in the function. By doing the whole declaration and assignment outside the function, you get what you're looking for. Which I assume (based on the title of your question) is actually namespacing and not some arbitrary global variable. That would be naughty! ;-)

UPDATE:

Oh, and by the way, a better way to do this might be by explicitly referencing the global object:

(function (exports) {
    exports.n = exports.n || {};
}(this));

This will probably play better with things like node.js and anything that might wrap your code (like a $(function() { })).

Upvotes: 3

Eugene Naydenov
Eugene Naydenov

Reputation: 7295

1) Small change would help:

(function() {
    var n = window.n = window.n || {};
    // Code of your module...
    n.hello = function() {
        alert('Hello');
    };
})();

n.hello();

DEMO

In this way you have n, visible globally and a local reference n to the same object inside your function.

2) I'm suggesting also you to redesign it in next way.

Make each module like:

var MyApplication = (function(app) {

    // Code of the module here...

    app.hello = function() {
        console.log('Hello!');
    };

    return app;
})(MyApplication || {});

MyApplication.hello();

Upvotes: 1

jbabey
jbabey

Reputation: 46657

You've encountered the difference between an undefined variable and a defined variable with an undefined value.

Referencing an undefined variable will cause an exception (ReferenceError: n is not defined)

Referencing a defined variable with an undefined value is fine and will cast to false in a condition.

To avoid this, you can reference n as a property of the window object, as a property can never be undefined, only have an undefined value.

(function(){
...
window.n = window.n || {};
...
})(); 

Upvotes: 2

Blender
Blender

Reputation: 298326

n has not been declared yet, so checking whether n is true-like won't work. You can check explicitly to see if n has been declared yet:

n = typeof n !== 'undefined' ? n : {};

But that isn't very nice to look at. To place something in the global scope, use window.n in place of n or declare n outside of your function:

var n;

(function(){
...
n = n || {};
...
})(); 

Upvotes: 1

Michał Miszczyszyn
Michał Miszczyszyn

Reputation: 12711

var creates local variable while not using it pollutes global namespace. Browsers should prevent this as mostly you don't want to create a global variable.

What you can do is using window object:

window.n = window.n || {}

But this will work only in browser environment. If it's not the case (eg. you're using serverside JavaScript) then retrieve the global object this way:

var global = (function getGlobal(){
    return (function(){
        return this;
    }).call(null);
}());

and then:

global.n = global.n || {};

Upvotes: 0

Michael Krelin - hacker
Michael Krelin - hacker

Reputation: 143169

You can add var n; before your code and you will have global n. var matters because it makes n local to the function.

Upvotes: 0

zzzzBov
zzzzBov

Reputation: 179136

With strict mode you need to explicitly use the global namespace, which is typically window; changing the line to use window.n should get you what you want:

window.n = window.n || {};

Upvotes: 0

Russell Durham
Russell Durham

Reputation: 624

At the end of the function do:

window.n = n;

That will place n in the global namespace allowing you to use it in other scripts.

Upvotes: 0

Related Questions