Question

I'm trying to get a function to set a variable at top-level scope of a module, but not have it leak into global scope (because... bad). At first I thought implicit variable declarations in a module would stay in the module, but it seems like it goes to the script calling the module too.

Take for example (aModule.node.js):

var
  onlyToThisModule = true;

function createAGlobal() {
  //creates a implicit var unfortunately
  globalToEveryone = true;
}

createAGlobal(); //this will now appear in the global scope for the script that require() it. eeek.

console.log(onlyToThisModule);

exports = createAGlobal;

Then require it:

var
  aModule = require('./aModule.node.js');

console.log(globalToEveryone); //outputs "true"
console.log(typeof onlyToThisModule); //outputs "undefined"

What I would like to do is to declare a variable in createAGlobal that would not leak into the global scope. I'd like to keep it in the scope of the function that called it. I know I can put it in the var statement of the module itself, but I'm looking to declare the variables only if someone executes the function. Is there a way to achieve this type of scoping?

Was it helpful?

Solution 3

Alright. Thanks for the answer attempts, but after much annoyance and research, I don't think it is possible to do exactly what I was trying to do, but I got close enough for my project.

Dead ends: I was able to access the contents of variables up the scope chain using V8's stack trace and faking an error. I couldn't manage to write to the scope up the chain though. Plus that is somewhat insane. I also went down the route of trying to do some Object.defineProperty trickery, but I never could get that to work exactly as needed.

For my purpose, it works to put everything into an object then use with which is slightly less evil than globals. For the situation I need, it works fine. So, like this:

var local = { 
    a : '1',
    b : '2',
    c : '3'
};

function contained() {
    with (local) {
        console.log(a,b,c);
    }
}
console.log(typeof a) //undefined
contained();

Now I'll just pass the local object through the module and have the script or other module use with where needed. Not quite as syntactically pure as I would have liked. I also think that implicit variables should be local only to the module since you can access globals using the global.foo (I know, not going to happen, would break the internet and so on)

OTHER TIPS

Why is it a requirement that the variable only gets declared if someone calls the function? If that's not a strict requirement, then you could declare it in your module scope, and initialize it in your function scope:

var onlyToThisModule; // currently `undefined`

function initVar() {
    onlyToThisModule = true;
}

console.log(onlyToThisModule); // undefined
initVar();
console.log(onlyToThisModule); // true

... if, on the other hand, you really want to declare a variable only in the function but also have it be available to the rest of your module, then you can create it as a class property:

function myClass() {
    this.onlyToThisModule = true;
}

var mc = new myClass();
console.log(mc.onlyToThisModule);

... but this would still expose the variable to the calling module, just through the intermediary of your function.

If neither of those suits your situation, then you'll have to provide more details.

When you define a variable without using var it is put into global scope and has some other nuances beyond the scope of this comment. It is not an 'implicit var'.

Anyway, the module itself can be accessed from anywhere through require(), so you don't need to make anything global:

In aModule

exports.globalToEveryone = true;

In anotherModule

var globalVal = require('aModule').globalToEveryone;

To expand: The value you want to access from everywhere almost certainly should not be global if you want to control access to it. Probably a better answer for your purposes is to use an accessor method:

var globalToEveryone = undefined;
exports.globalToEveryone() = function() {
    if (globalToEveryone !== undefined) {
        return globalToEveryone;
    } else {
        // what happens if it hasn't been set yet?
    }
}

Elsewhere in your code when you are interested in the value, you would use

var localVersion = require('aModule').globalToEveryone();

You will find that although you have to type a little more to get to the value, you will have much more maintainable code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top