How to deal with cyclic dependencies in Node.js

2019-01-01 03:49发布

I've been working with nodejs lately and still getting to grips with the module system so apologies if this is an obvious question. I want code roughly like the following below:

a.js (the main file run with node)

var ClassB = require("./b");

var ClassA = function() {
    this.thing = new ClassB();
    this.property = 5;
}

var a = new ClassA();

module.exports = a;

b.js

var a = require("./a");

var ClassB = function() {
}

ClassB.prototype.doSomethingLater() {
    util.log(a.property);
}

module.exports = ClassB;

My problem seems to be that I can't access the instance of ClassA from within an instance of ClassB.

Is there a correct / better way to structure modules to achieve what I want? Is there a better way to share variables between modules?

12条回答
冷夜・残月
2楼-- · 2019-01-01 04:00

A solution which require minimal change is extending module.exports instead of overriding it.

a.js - app entry point and module which use method do from b.js*

_ = require('underscore'); //underscore provides extend() for shallow extend
b = require('./b'); //module `a` uses module `b`
_.extend(module.exports, {
    do: function () {
        console.log('doing a');
    }
});
b.do();//call `b.do()` which in turn will circularly call `a.do()`

b.js - module which use method do from a.js

_ = require('underscore');
a = require('./a');

_.extend(module.exports, {
    do: function(){
        console.log('doing b');
        a.do();//Call `b.do()` from `a.do()` when `a` just initalized 
    }
})

It will work and produce:

doing b
doing a

While this code will not work:

a.js

b = require('./b');
module.exports = {
    do: function () {
        console.log('doing a');
    }
};
b.do();

b.js

a = require('./a');
module.exports = {
    do: function () {
        console.log('doing b');
    }
};
a.do();

Output:

node a.js
b.js:7
a.do();
    ^    
TypeError: a.do is not a function
查看更多
时光乱了年华
3楼-- · 2019-01-01 04:03

Actually I ended up requiring my dependency with

 var a = null;
 process.nextTick(()=>a=require("./a")); //Circular reference!

not pretty, but it works. It is more understandable and honest than changing b.js (for example only augmenting modules.export), which otherwise is perfect as is.

查看更多
美炸的是我
4楼-- · 2019-01-01 04:04

Similar to lanzz and setect's answers, I have been using the following pattern:

module.exports = Object.assign(module.exports, {
    firstMember: ___,
    secondMember: ___,
});

The Object.assign() copies the members into the exports object that has already been given to other modules.

The = assignment is logically redundant, since it is just setting module.exports to itself, but I am using it because it helps my IDE (WebStorm) to recognise that firstMember is a property of this module, so "Go To -> Declaration" (Cmd-B) and other tooling will work from other files.

This pattern is not very pretty, so I only use it when a cyclic dependency issue needs to be resolved.

查看更多
余欢
5楼-- · 2019-01-01 04:08

An other method I've seen people do is exporting at the first line and saving it as a local variable like this:

let self = module.exports = {};

const a = require('./a');

// Exporting the necessary functions
self.func = function() { ... }

I tend to use this method, do you know about any downsides of it?

查看更多
笑指拈花
6楼-- · 2019-01-01 04:10

Try to set properties on module.exports, instead of replacing it completely. E.g., module.exports.instance = new ClassA() in a.js, module.exports.ClassB = ClassB in b.js. When you make circular module dependencies, the requiring module will get a reference to an incomplete module.exports from the required module, which you can add other properties latter on, but when you set the entire module.exports, you actually create a new object which the requiring module has no way to access.

查看更多
旧时光的记忆
7楼-- · 2019-01-01 04:10

for your problem, you can use function declarations.

class-b.js:

var ClassA = require('./class-a')

module.exports = ClassB

function ClassB() {

}

class-a.js:

var classB = require('./class-b')

module.exports = ClassA

function ClassA() {

}
查看更多
登录 后发表回答