user1585789
user1585789

Reputation: 646

Nodejs and implicit global variables?

I'm using some external libraries intended to be used in a browser and they set global variables implicitly like a='a' (without the var).

It seems like when I require certain scripts that do this, sometimes the variable will be accessible outside its scope just like in a browser, but for other scripts the global variable is not accessible outside its own script.

Anyone know how nodejs handles implicit global variables, and why I'm seeing somewhat random behavior? I found surprisingly little on the internet.

I can go into the scripts. write something like

if(typeof exports !== 'undefined' && this.exports !== exports){
            var GLOBAL=global;
        }
        else{
                var GLOBAL=window;
            }

and then change all implicit references to GLOBAL.reference but these scripts are not my own and every time I want to get the latest version of them I would have to do this over again, which is clearly not desirable.

Using module.exports would be cleaner because then I don't have change all the references, but just add a section of the top of every file that exports the globals, but my original question about how node handles implicit globals is still relevant

Upvotes: 0

Views: 1615

Answers (3)

Edwin Dalorzo
Edwin Dalorzo

Reputation: 78639

I am not sure if this answer will help you, since it is hard to diagnose what is going on with your code, but maybe, some of this reasonings can help you diagnose the actual problem in your code.

The behavior in node is actually similar to that of the browser. If you would declare a variable without the var keyword the variable will be accesible through the global object.

//module foo.js
a = 'Obi-wan';

//module bar.js
require('./foo');
console.log(global.a); //yields Obi-wan
console.log(a); //yields Obi-wan

It is not clear why you say this behavior is not consistent in your code, but if you think about it, the use of global variables is precisely subject to this kind of problems since they are global and everyone could overwrite them at any time, causing as a result this unexpected conditions.

There is one aspect in which node is different from the browser though and that could be affecting the behavior that you see.

In the browser, if you do something like this directly in a JavaScript file:

console.log(this==window); //yields true

But if you do the same thing in a Node.js module:

console.log(this==global); //yields false

Basically, in the outer scope of a Node.js module the this reference points to the current module.exports object.

console.log(this==exports); //yield true

So, chances are that if you are putting data in the global scope (window) in the browser through the use of this, you may end up with a module scope in Node.js instead.

Interestingly, the code inside a function in Node.js behaves pretty much as in the browser, in terms of the use of the global scope.

(function what(){
   console.log(this==global); //yields true
})();

Upvotes: 1

user1585789
user1585789

Reputation: 646

Ah, the problem was that the global variable that was being declared globally in the browser wasn't being declared via a='a', but with var a='a'. In a browser if the var keyword is used not inside a function it will still declare a global variable. It only declares a local variable if the var keyword is inside a function. Node.js doesn't behave this way, and all var declarations are considered local.

Its ashame node.js does this, it makes it less compatible with browser scripts, for no real reason. (other than allowing people not to have to wrap all their scripts in a function).

Upvotes: 0

Vinz243
Vinz243

Reputation: 9958

This does not directly answer your question but it provides a solution since I don't think it is possible.

I love regexp. They are so powerful:

js = js.replace(/^(\t|\s{4})?(var\s)?(\w+)\s=/gm, function () {
    if (arguments[1] || arguments[2]) return (arguments[1] || '') + (arguments[2] || '')  + arguments[3] + ' =';
    return 'exports.' + arguments[3] + ' =';
});*

JSFiddle here

How does it work? I will retrace my work:

  1. /(\w+)\s=/g will take any var, return 'exports.' + arguments[1] + ' ='; will turn them into an export. Not very good.
  2. /(var\s)?(\w+)\s=/g will take any var, but in the callback we examined first group (var\s). Is it undefined? Then we should export it, else nothing should happen. But what about scopes?
  3. /^(\t|\s{4})?(var\s)?(\w+)\s=/gm now we use indent to determine the scope :)

You should be able to run this regex on your file. Be careful, you need it properly indented, and be aware that I might have forgotten some things.

Upvotes: 0

Related Questions