5

I have read a lot of articles about how to create modules in node.js, and you can use module.exports to expose module internals to the file which includes it.. awesome!

How does this work the other way around? I'll use the following as an example :-)

USER.JS

 function User() {
   this.property = 'value';
   this.doSomething = function() {
     var getStuff = mainFileFunction();
     // do something with getStuff
 }

 module.exports = User;

MAIN.JS

 var myArray = [];
 myArray.push('something');
 myArray.push('something else');
 mainFileFunction() {
   for(thing in myArray) {
     return myArray[thing];
   }
 }

 var u = new user();

 log(u.property);  <---  THIS IS EXPOSED, COOL!

 u.doSomething();  <--- This will throw an error because mainFileFunction is not defined in user.js :-(

If I were to move mainFileFunction to the user file, then it still wouldn't work because the myArray array wouldn't be defined... and if I were to move that over too, I wouldn't be able to use it in other functions in main (which I want to) :-)

I'm sorry if I'm missing something really obvious here... What I want is to expose the parts of my choosing from modules I include (module.export works for that) but I also want to expose everything from the main file to all the includes..

or just expose everything to everything? is that totally messy and horrible??

Just to explain what I am trying to do here... I want to have classes defined in separate files, but I want to instantiate a bunch of them as objects in the main file and store them in arrays.. I want the objects to contain methods which can access arrays of the other object types.

Thanks guys! :-)

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
Andy Phillips
  • 247
  • 1
  • 4
  • 14
  • This migth be relevant: http://stackoverflow.com/questions/4140661/global-variables-for-node-js-standard-modules – hugomg Aug 27 '13 at 20:20
  • Why don't you simply pass the `stuff` as a parameter into `doSomething` from the main file? – Bergi Aug 27 '13 at 20:27
  • @Bergi I assume this is a self contained example about the use of something like the strategy pattern in NodeJS between modules. There might be a lot more logic involved than shown above, for example `doSomething` itself might be invoked on every element on an array, or used in deferred execution. – Benjamin Gruenbaum Aug 27 '13 at 20:28
  • Side note, don't use `for ... in` loops on arrays, those are for objects. Instead use `array.forEach` or plain ol' `for` loops. (Although in your question, you're returning the _first_ property, and there is no guarantee on what the 'first' property of an object is) – Benjamin Gruenbaum Aug 27 '13 at 20:34
  • You're experiencing a closure. Or javascript's pseudo private functions. – Seth Aug 27 '13 at 20:36
  • OP - A reference to my answer would be appreciated. Did it solve your issue? What did you end up doing to solve this? – Benjamin Gruenbaum Sep 08 '13 at 10:44

4 Answers4

7

You can use globals, or have a proper circular dependency (requireing both files), however - this is usually a bad habit which can lead to maintainability problems in the future.

Instead, you can use dependency injection and inject doSomething into your module. This basically gives you the following for free:

  • You can test User with a simple mock implementation of doSomething later and verify the correctness of your code
  • The dependencies of a user are explicit and not implicit, which makes it obvious what a user needs.

I'll provide two implementations, one using constructor dependency injection and one with a module wide setting.

 USER.JS

 function User(dependentFunction) {
   this.property = 'value';
   this.doSomething = function() {
     var getStuff = dependentFunction();
     // do something with getStuff
   }
 }

 module.exports = User;

MAIN.JS
...
var u = new User(mainFileFunction);
u.doSomething(); // this will now work, using mainFileFunction

What happens here is fairly simple, and we know what's going on.

This can also be a module wide setting

USER.JS

function User(depFunc) {
  this.property = 'value';
  this.doSomething = function() {
    var getStuff = depFunc();
    // do something with getStuff
  }
}
function UserFactory(depFunc){ 
    return function(){
        return new User(depFunc);
    }
}
module.exports = UserFactory;

MAIN.JS

 var getUser = UserFactory(mainFileFunction);
 var u = getUser(); // will return a new user with the right function
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • 1
    +1 for explanation and dependency injection but just have a doubt - in the last line where is `uf()` defined ? or I missed something very basic – exexzian Aug 28 '13 at 05:41
  • 1
    @sansix Nice catch! That was supposed to be `getUser` , I started by calling it uf for user factory but I've decided that I don't like short undescriptive names :) – Benjamin Gruenbaum Aug 28 '13 at 06:00
1

+1 to Benjamin answer for dependency injection. I would like to add another way to inject objects in your modules by passing the dependency in the require like require('./module.js')(dependentFunction);

//MAIN.js
var db = function() {
    this.rows = [];
    this.insert = function(name) {
        this.rows.push(name);
        console.log('Db: Inserting user with name ' + name);
    }
    this.getAll = function(){
        return this.rows;
    }
}
var fakeDb = new db();
var user = require('./user.js')(fakeDb);
user.add('Jhon');
user.add('Rose');
user.list();

and

//users.js
module.exports = function(db) {
    return {
        add: function(name) {
            db.insert(name);
        },
        list: function() {
           var users =  db.getAll();
           var i = users.length;
           console.log('listing users\n-------');
           while(i--) {
               console.log(users[i]);
           }
        }
    }
}
alfonsodev
  • 2,714
  • 2
  • 23
  • 30
0

You should pass mainFileFunction as a parameter to the constructor of user.

USER.JS

 function User(mainFileFunction) {
   this.property = 'value';
   this.doSomething = function() {
     var getStuff = mainFileFunction();
     // do something with getStuff
 }

 module.exports = User;

In your main.js use the following

var u = new user(mainFileFunction);

Charlie Brown
  • 2,817
  • 2
  • 20
  • 31
0

How about moving mainFileFunction to user.js, and have the function accept an array as an argument:

mainFileFunction(array) {
   for(thing in array) {
     return array[thing];
   }
 }

And then when you call it from main.js, pass the function your array:

u.doSomething(myArray);
levi
  • 23,693
  • 18
  • 59
  • 73
  • This will lose genericity, instead of accepting a function we're now accepting a data type effectively losing the strategy pattern we've previously had. – Benjamin Gruenbaum Aug 27 '13 at 20:26
  • Op states "I want the objects to contain methods which can access arrays of the other object types.". So the expected type will always be an array, what is being lost? – levi Aug 27 '13 at 20:32