Search code examples
node.jsnode-modules

node.js module export how to use data among all modules?


I would like to use updated (and only then) globals among all node modules. How to do that? Questions are in code.

app.js

var data = 'data';

var global = require('glob.js')(data);
// here we are require your globals variables and we corectly 'set them'

console.log(globals.glob1);
// we can use them here

glob.js

module.exports  = function(data)
{
    var globs = {
        glob1 : data.toLowerCase(),
        glob2 : data.toUpperCase()
    }
    return globs;
}

mod.js

var global = require('glob.js'); // I require globals but they are not set...

function funct(someOtherData, someMoreData)
{
    var test = global.glob1;
    console.log(test);
    // why I can't use globals here ? How can I use corectly set globals (globals need to be updated first - app.js, then ALL other modules should be able to use correctly set globals)?
}

module.export = funct;

Solution

  • For the answer scroll down to the TLDR section below but do read on to understand why.

    Part1 - the difference between a function and a function call

    Your first mistake is that you are exporting a function, not an object:

    module.exports  = function(data) // <---- this is a function
    {
        var globs = {
            glob1 : data.toLowerCase(),
            glob2 : data.toUpperCase()
        }
        return globs;
    }
    

    and in app.js you do this:

    console.log(globs.glob1); <--- globs is a function, not an object
    

    when you should be doing this:

    console.log(globs().glob1);
    

    Why is this? OK, lets forget for a moment your module. Consider the following code:

    var a = function(){ return 2 };
    
    console.log(a);   // do you expect this to print a function or 2?
    console.log(a()); // what do you expect this to print?
    

    This is a very basic rule about functions in all programming languages, not just javascript: to get the return value you need to call the function. So in your code:

    function myExportedFunction (data) {
        // some logic here...
        return globs;
    }
    
    console.log(myExportedFunction);         // prints a function
    console.log(myExportedFunction());       // prints the globs object
    console.log(myExportedFunction().glob1); // prints value of glob1
    

    So it's simple really. There is no magic syntax going on. You've just forgotten to return the glob object and are using the function pointer instead. Obviously the function has no glob1 property so it's correct for it to be undefined.

    Part2 - function local variables

    OK. So let's say you made the changes I recommended above. There's an obvious problem with the way the function was written. What happens when you do this:

    var glob = require('glob.js')();
    console.log(glob.glob1); // <--- prints "undefined"
    

    So the first problem is you're not checking if you're passing data or nothing. So every time you call the function you will overwrite the stored value.

    There's another problem, you are always returning a different object every time you call the function. Let's look at how local variables work when returned:

    function a () {
        var data = {}
        return data;
    }
    
    var x = a();
    var y = a();
    
    x.testing = 1;
    y.testing = 2;
    
    console.log(x.testing); // prints 1
    console.log(y.testing); // prints 2
    

    So, every time you call a function that creates a local variable you are returning a different object. Actually what's doing this is not really the variable but the object literal syntax:

    var a = {};
    // is basically the same as
    var a = new Object();
    

    If we change the above example to:

    function a () {
        return {};
    }
    

    it would still behave the same.

    TLDR

    So, how do we fix it? Simple, create the object outside of the function and check if we pass data to initialize:

    var globs = {
        glob1 : "",
        glob2 : ""
    }
    
    module.exports  = function(data)
    {
        globs.glob1 = data.toLowerCase();
        globs.glob2 = data.toUpperCase();
    
        return globs;
    }
    

    Now everything should work:

    In app.js

    var global = require('glob.js')(data);
    

    In mod.js

    var global = require('glob.js')();
    

    Epologue - modules are singletons

    It may or may not be obvious to you why the above should work. In case you already know why I'm writing this as reference to future readers.

    In node.js modules are implemented as proper singletons. Therefore in node if you want a singleton all you need to do is write a module, you don't need to implement any special code for it.

    What this means is that all module globals (module scoped variables) are shared amongst all requires. Here's a very simple module to share one variable amongst all modules:

    shared.js

    var x = "";
    
    module.exports = {
        set: function (val) {x=val},
        get: function () {return x}
    }
    

    a.js

    var shared = require('./shared');
    
    shared.set("hello world");
    

    b.js

    var shared = require('./shared');
    
    console.log(shared.get()); // prints "hello world"
    

    We're using this feature to declare a shared glob variable in the code above.