why module level variables are bad?

The calling node will assign to these arguments when it will invoke the wrapper function.
This is what makes them look as if they are globals in the scope of your node module.
It seems we have globals in our module however:
– export is defined as a reference to module.exports prior to that.
– require and module, are defined by the function executed.
– __filename and __dirname are the filename and folder of your current module.

caching – a double edge sword

Node will then cache this module, so the next time you require the file, you won’t actually get a fresh copy, but you’ll be getting the same object as before.
This means you’ll be using the same global modules variables in multiple places, which means danger!

Here is a code example that illustrated the problem:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//moduletest.js

'use strict';

varx=0;

module.exports=function(val){

console.log(`val:${val},x:${x}`);

if(val!==x&&x!==0)thrownewError(`failure!!!${x}!=${val}`);

x=val;

}

//main.js

constfn1=require('./moduletest');

constfn2=require('./moduletest');

setInterval(function(){

fn1('a');

},200

);

setInterval(function(){

fn2('b');

},50

);

I’m running here two calls to the same function, with a small delay between each call, after a few runs we will notice that the function will run over each others variables. Which is an example of a module global issue.

How to solve globals?

There are multiple potential solutions to this global issue, I’ll present you with two potential solutions

Solution 1 – Functional

If we define a local scope inside our module, we can return a new set of variables for each run.
We will use a ‘let’ keyword, along with a scoped function (not needed, but nicer and better scope control).

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

//testmodule.js

'use strict';

module.exports=(function(){

letx=0;

returnfunction(val){

console.log(`val:${val},x:${x}`);

if(val!==x&&x!==0)

thrownewError(`failure!!!${x}!=${val}`);

x=val;

}

});

//main.js

fn1=require('./testmodule')();//<--- calling a function each time

fn2=require('./testmodule')();

// fn1 and fn2 are new functions with new variables, we busted the cache !! :)

// notice I also use let, to ensure scope variables, and not hoisted vars.

Solution 2 – use Classes

We can just define a class then create a new class for each run.
This way each variable is a private member of that class, ensuring proper encapsulation.