如何处理Node.js中的循环依赖关系

如何处理Node.js中的循环依赖关系,node.js,module,require,circular-dependency,cyclic-dependency,Node.js,Module,Require,Circular Dependency,Cyclic Dependency,我最近一直在与nodejs合作,并且仍然在处理模块系统,因此如果这是一个明显的问题,我深表歉意。我希望代码大致如下所示: a.js(主文件与节点一起运行) b.js var a = require("./a"); var ClassB = function() { } ClassB.prototype.doSomethingLater() { util.log(a.property); } module.exports = ClassB; var ClassB = functio

我最近一直在与nodejs合作,并且仍然在处理模块系统,因此如果这是一个明显的问题,我深表歉意。我希望代码大致如下所示:

a.js(主文件与节点一起运行)

b.js

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

var ClassB = function() {
}

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

module.exports = ClassB;
var ClassB = function() {
}

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

module.exports = ClassB;

var a = require("./a"); // <------ this is the only necessary change
_ = 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 
    }
})
b = require('./b');
module.exports = {
    do: function () {
        console.log('doing a');
    }
};
b.do();
a = require('./a');
module.exports = {
    do: function () {
        console.log('doing b');
    }
};
a.do();
class ClassA {

    constructor(){
        ClassB.someMethod();
        ClassB.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class A Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassA;
var ClassB = require( "./classB.js" );

let classX = new ClassA();
class ClassB {

    constructor(){
        ClassA.someMethod();
        ClassA.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class B Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassB;
var ClassA = require( "./classA.js" );

let classX = new ClassB();
我的问题似乎是我无法从ClassB实例中访问ClassA实例

是否有正确/更好的方法来构建模块以实现我的目标?
有没有更好的方法在模块之间共享变量?

而node.js确实允许循环的
require
依赖项,正如您发现的那样,您最好重新构造代码,使其不需要。也许可以创建第三个类,使用其他两个类来完成您需要的任务。

尝试在
模块上设置属性。导出
,而不是完全替换它。例如,
a.js中的
module.exports.instance=new ClassA()
b.js中的
module.exports.ClassB=ClassB
。当您建立循环模块依赖关系时,需要的模块将从需要的模块中获得对不完整的
模块的引用。导出
,您可以在后面添加其他属性,但当您设置整个
模块。导出
,则,实际上,您创建了一个新对象,而所需模块无法访问该对象。

有时引入第三类(正如JohnnyHK建议的那样)实际上是人为的,因此除了Ianzz之外: 如果您确实希望替换module.exports,例如,如果您正在创建一个类(如上例中的b.js文件),也可以这样做,只需确保在启动循环require的文件中,“module.exports=…”语句发生在require语句之前

a.js(主文件与节点一起运行)

b.js

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

var ClassB = function() {
}

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

module.exports = ClassB;
var ClassB = function() {
}

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

module.exports = ClassB;

var a = require("./a"); // <------ this is the only necessary change
_ = 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 
    }
})
b = require('./b');
module.exports = {
    do: function () {
        console.log('doing a');
    }
};
b.do();
a = require('./a');
module.exports = {
    do: function () {
        console.log('doing b');
    }
};
a.do();
class ClassA {

    constructor(){
        ClassB.someMethod();
        ClassB.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class A Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassA;
var ClassB = require( "./classB.js" );

let classX = new ClassA();
class ClassB {

    constructor(){
        ClassA.someMethod();
        ClassA.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class B Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassB;
var ClassA = require( "./classA.js" );

let classX = new ClassB();
var ClassB=function(){
}
ClassB.prototype.doSomethingLater(){
util.log(a.property);
}
module.exports=ClassB;

变量a=要求(“./a”);// [编辑]现在不是2015年,大多数库(即express)都使用更好的模式进行了更新,因此不再需要循环依赖关系。我建议不要使用它们


我知道我在挖掘一个古老的答案。。。 这里的问题是module.exports是在您需要ClassB之后定义的。 (JohnnyHK的链接显示) 循环依赖关系在节点中非常有效,它们只是同步定义的。 如果使用得当,它们实际上解决了许多常见的节点问题(比如从其他文件访问express.js
app

在需要具有循环依赖关系的文件之前,请确保定义了必要的导出

这将打破:

var ClassA = function(){};
var ClassB = require('classB'); //will require ClassA, which has no exports yet

module.exports = ClassA;
这将有助于:

var ClassA = module.exports = function(){};
var ClassB = require('classB');
我一直使用此模式访问其他文件中的express.js
app

var express = require('express');
var app = module.exports = express();
// load in other dependencies, which can now require this file and use app

需要最少更改的解决方案是扩展
模块。导出
而不是覆盖它

a.js-使用b.js中方法do的应用程序入口点和模块*

_ = 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-使用a.js中方法do的模块

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

var ClassB = function() {
}

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

module.exports = ClassB;
var ClassB = function() {
}

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

module.exports = ClassB;

var a = require("./a"); // <------ this is the only necessary change
_ = 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 
    }
})
b = require('./b');
module.exports = {
    do: function () {
        console.log('doing a');
    }
};
b.do();
a = require('./a');
module.exports = {
    do: function () {
        console.log('doing b');
    }
};
a.do();
class ClassA {

    constructor(){
        ClassB.someMethod();
        ClassB.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class A Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassA;
var ClassB = require( "./classB.js" );

let classX = new ClassA();
class ClassB {

    constructor(){
        ClassA.someMethod();
        ClassA.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class B Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassB;
var ClassA = require( "./classA.js" );

let classX = new ClassB();
它将发挥作用并产生:

doing b
doing a
虽然此代码不起作用:

a.js

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

var ClassB = function() {
}

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

module.exports = ClassB;
var ClassB = function() {
}

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

module.exports = ClassB;

var a = require("./a"); // <------ this is the only necessary change
_ = 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 
    }
})
b = require('./b');
module.exports = {
    do: function () {
        console.log('doing a');
    }
};
b.do();
a = require('./a');
module.exports = {
    do: function () {
        console.log('doing b');
    }
};
a.do();
class ClassA {

    constructor(){
        ClassB.someMethod();
        ClassB.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class A Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassA;
var ClassB = require( "./classB.js" );

let classX = new ClassA();
class ClassB {

    constructor(){
        ClassA.someMethod();
        ClassA.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class B Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassB;
var ClassA = require( "./classA.js" );

let classX = new ClassB();
b.js

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

var ClassB = function() {
}

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

module.exports = ClassB;
var ClassB = function() {
}

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

module.exports = ClassB;

var a = require("./a"); // <------ this is the only necessary change
_ = 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 
    }
})
b = require('./b');
module.exports = {
    do: function () {
        console.log('doing a');
    }
};
b.do();
a = require('./a');
module.exports = {
    do: function () {
        console.log('doing b');
    }
};
a.do();
class ClassA {

    constructor(){
        ClassB.someMethod();
        ClassB.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class A Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassA;
var ClassB = require( "./classB.js" );

let classX = new ClassA();
class ClassB {

    constructor(){
        ClassA.someMethod();
        ClassA.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class B Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassB;
var ClassA = require( "./classA.js" );

let classX = new ClassB();
输出:

node a.js
b.js:7
a.do();
    ^    
TypeError: a.do is not a function

解决方案是在需要任何其他控制器之前“向前声明”导出对象。因此,如果您以这种方式构建所有模块,并且不会遇到任何类似的问题:

//模块导出转发声明:
module.exports={
};
//控制器:
var other_module=require('./其他_module');
//职能:
var foo=函数(){
};
//模块导出和注入:
module.exports.foo=foo;

重要的是不要重新分配
模块。导出已给您的
对象,因为该对象可能已在循环中给了其他模块!只需在
模块内分配属性。导出
和其他模块将看到它们出现

因此,一个简单的解决方案是:

module.exports.firstMember=\;
module.exports.secondMember=\uuuuuuuuuux;
唯一的缺点是需要重复多次
module.exports.


与lanzz和setec的答案类似,我一直在使用以下模式,这让我感觉更具陈述性:

module.exports=Object.assign(module.exports{
第一名成员:,
第二名成员:,
});
Object.assign()
将成员复制到已提供给其他模块的
exports
对象中

=
分配在逻辑上是多余的,因为它只是设置
模块。将
导出到自身,但我之所以使用它,是因为它帮助我的IDE(WebStorm)识别
第一个成员
是该模块的属性,所以“转到->声明”(Cmd-B)和其他工具将从其他文件工作

这个模式不是很漂亮,所以我只在需要解决循环依赖性问题时使用它

它非常适合于,因为您可以轻松地从对象添加和删除导出,特别是在使用ES6时


只有在你需要的时候,你才需要呢?因此,您的b.js如下所示

var ClassB = function() {
}
ClassB.prototype.doSomethingLater() {
    var a = require("./a");    //a.js has finished by now
    util.log(a.property);
}
module.exports = ClassB;

当然,最好将所有require语句放在文件的顶部。但有时,我原谅自己从一个不相关的模块中挑选了一些东西。称之为黑客,但有时这比引入进一步的依赖关系、添加额外的模块或添加新的结构(EventEmitter等)要好。

事实上,我最终需要使用

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

虽然不漂亮,但很管用。这比更改b.js(例如,仅增强modules.export)更容易理解和更诚实,否则它就是完美的

我见过的另一种方法是在第一行导出并将其保存为局部变量,如下所示:

let self = module.exports = {};

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

// Exporting the necessary functions
self.func = function() { ... }
我倾向于使用这种方法,你知道吗