Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'TypeError: undefined is not a function' when jumping 'tween modules

I keep getting this issue in Node where my application crashes whenever I'm calling functions from one another.

I've made this minimum working example (working as in it gives me the error):

Start module

var module2 = require('./module2');

var data = 'data';

module2.doStuff(data);

Module2

var module3 = require('./module3');

function doStuff(data){
    // Stuff happens to 'data'
    module3.takeStuff(data);
}

function doSomethingElse(data){
    console.log(data);
}


module.exports = {
    doStuff: doStuff,
    doSomethingElse: doSomethingElse
};

Module3

var module2 = require('./module2');

function takeStuff(data){
    // Stuff happens to 'data'
    module2.doSomethingElse(data); // I get the type error here
}

module.exports = {
    takeStuff: takeStuff
};

The error I get is:

module2.doSomethingElse(data); // I get the type error here
        ^
TypeError: undefined is not a function

The start module calls a function in module2 which ultimately calls for a function in module3, which in turn calls for a function in module2.

All modules are properly required and it finds the first method in module2 just fine.

What's happening here and how would one go about this pattern when one needs to get a function from the module one came with?

EDIT

Debugging shows me that the module exists, but it's empty apart from the prototype it has. My question is why? Inside of Node/JavaScript, what's happening here?

like image 329
Gemtastic Avatar asked Aug 03 '15 09:08

Gemtastic


1 Answers

This issue here can be easily fixed, while keeping the structure of your app (which is fine, in regard to circular references).

You just need need to keep the default exports object that is given to your modules by the system. Do not change it with module.exports = {...}.

The following should work:

Start module

var module2 = require('./module2');

var data = 'data';

module2.doStuff(data);

Module 2

var module3 = require('./module3');

exports.doStuff = function doStuff(data){
    // Stuff happens to 'data'
    module3.takeStuff(data);
};

exports.doSomethingElse = function doSomethingElse(data){
    console.log(data);
};

Module 3

var module2 = require('./module2');

exports.takeStuff = function takeStuff(data){
    // Stuff happens to 'data'
    module2.doSomethingElse(data); // I get the type error here
};

Explanation

I'll try to explain what happens from the first line of your starting point:

  1. In start.js, you require module2 and it is not loaded yet: the code from module2.js is executed
  2. In module2.js, you require module3 and it is not loaded yet: the code from module3.js is executed
  3. In module3.js, you require module2, and it is already loaded: the module2 variable now contains the exports object from module2.
  4. The rest of module3 is executed and you change its exports object with module.exports = {...}
  5. In module2.js, the module3 variable now contains the exports object from module3.
  6. The rest of module2.js is executed and you change its exports object with module.exports = {...}
  7. In start.js, the module2 variable now contains the exports object from module2.

The issue here is between points 3 and 6. Module3 is receiving the exports of module2 (3) before you change the reference to it (6). Using exports.method = ... solves the problem because the exports object never changes.

like image 198
Targos Avatar answered Oct 05 '22 22:10

Targos