我最近一直在使用nodejs,仍然在掌握模块系统,所以如果这是一个明显的问题,那么很抱歉。我希望代码大致如下:

A.js(主文件与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;

我的问题似乎是我不能从ClassB的实例中访问ClassA的实例。

有什么正确的/更好的方法来构建模块来实现我想要的? 是否有更好的方法在模块间共享变量?


当前回答

需要最小变化的解决方案是扩展模块。导出而不是重写它。

A.js -应用程序入口点和模块,使用方法做从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 -模块使用方法做从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 
    }
})

它将工作和生产:

doing b
doing a

虽然这段代码将不起作用:

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();

输出:

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

其他回答

解决方案是在需要任何其他控制器之前“向前声明”导出对象。所以如果你像这样构建你所有的模块,你就不会遇到这样的问题:

// Module exports forward declaration:
module.exports = {

};

// Controllers:
var other_module = require('./other_module');

// Functions:
var foo = function () {

};

// Module exports injects:
module.exports.foo = foo;

如果你不能消除循环依赖(例如useraccount <——> userlogin),还有一个选择…

它就像使用setTimeout()一样简单

//useraccount.js

let UserLogin = {};

setTimeout(()=>UserLogin=require('./userlogin.js'), 10);

class UserAccount{
 
getLogin(){
return new UserLogin(this.email);

}

}



//userlogin.js

let UserAccount ={};

setTimeout(()=>UserAccount=require('./useraccount.js'), 15);


class UserLogin{

getUser(){

return new User(this.token);

}

}

有时引入第三个类(JohnnyHK建议)确实是人为的,所以除了Ianzz之外: 如果您确实需要更换模块。导出,例如,如果你正在创建一个类(如上面例子中的b.js文件),这也是可能的,只要确保在开始循环require的文件中,'模块。出口=…`语句发生在require语句之前。

A.js(主文件与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 ClassB = function() {
}

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

module.exports = ClassB;

var a = require("./a"); // <------ this is the only necessary change

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

所以一个简单的解决方案是:

module.exports.firstMember = ___;
module.exports.secondMember = ___;

唯一的缺点是需要重复module.exports。很多次了。


类似于lanzz和setec的回答,我一直在使用以下模式,它感觉更像是声明性的:

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

object .assign()将成员复制到已经分配给其他模块的exports对象中。

=赋值在逻辑上是冗余的,因为它只是一个设置模块。出口到自己,但我使用它,因为它帮助我的IDE (WebStorm)识别firstMember是这个模块的属性,所以“转到->声明”(Cmd-B)和其他工具将从其他文件工作。

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

它非常适合于显示模式,因为您可以轻松地从对象中添加和删除导出,特别是在使用ES6的属性简写时。

Object.assign(module.exports, {
    firstMember,
    //secondMember,
});

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

let self = module.exports = {};

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

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

我倾向于用这种方法,你知道它有什么缺点吗?