3

I'm having a problem accessing a field in an object that is required/imported at the top of a file, the field is accessible however when importing the object again at a later point in the code.


I've made a little example to show what I mean: [Execute code on CodingGround]

main.js:

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

ClassA.init();

ClassA.js:

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

var ClassA = function() {};

ClassA.init = function() {
    // Define a variable in ClassA
    this.myVar = 'My Value';

    // Create an instance of ClassB, and try to print the defined variable
    var myClass = new ClassB();
    myClass.printMyVar();
};

module.exports = ClassA;

ClassB.js:

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

var ClassB = function() {};

ClassB.prototype.printMyVar = function() {
    // Print the variable (not working)
    console.log('My Var: ' + ClassA.myVar);

    // Require ClassA again
    ClassA = require('./ClassA');

    // Print the variable again (working)
    console.log('My Var (again): ' + ClassA.myVar);
};

module.exports = ClassB;

I get the following output when main.js is executed:

My Var: undefined
My Var (again): My Value

Why does the first line show the variable is undefined, and why does it show the variable properly after requiring/importing the object again?

Is this caused by a circular dependency and if so, how can I avoid this problem in the future?

Tim Visée
  • 2,988
  • 4
  • 45
  • 55

1 Answers1

1

This is the situation described in the NodeJS modules "cycle" topic. The solution seems to be to augment the exports object rather than replacing it if circular dependencies are present.

Here's their example, modified to show that when b checks a.done later, it sees the correct value:

a.js:

console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

b.js:

console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
exports.check = function() {
    console.log('in b\'s check function, a.done = %j', a.done);
};
console.log('b done');

main.js:

console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);
b.check();

The output is:

main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true
in b's check function, a.done = true
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thanks, that is very helpful. I assume you mean that I should _add_ functions/fields to the `module.exports` instead of fully replacing it with, for example `ClassA`. If that's the case, how would you define the constructor of the class? I've asked a similar question in this comment: http://stackoverflow.com/questions/10869276/how-to-deal-with-cyclic-dependencies-in-node-js#comment64995389_10872988 – Tim Visée Aug 07 '16 at 13:22
  • 1
    @TimVisée: I think very much the way NodeJS itself does in some cases: A module defines a "namespace" if you will (the `exports` object), and individual classes are available as properties of that object rather than being top-level. E.g., like `fs.FSWatcher`, `fs.Stats`, etc. But they're not consistent: The events module replaces the `exports` object with `EventEmitter`. (I suspect ES2015's modules will solve this, but that could be wishful thinking on my part...) – T.J. Crowder Aug 07 '16 at 13:25
  • Thank you. I think I should try to avoid circular dependencies all together to keep everything _more modulair_. – Tim Visée Aug 07 '16 at 13:29
  • @TimVisée: I'm sure that's the best course where possible. :-) – T.J. Crowder Aug 07 '16 at 13:39