复杂的循环Node模块依赖抛出“TypeError:”inheritance“的超级构造函数必须有一个原型”

我有一个复杂的Node SDK项目,它使用一些类inheritance来尝试和static-ify Javascript。 我正在使用Node的模块caching行为来为SDK( Project类, ProjectClient的共享实例)创build一个类似于单例的行为。 初始化,它看起来像这样:

 var Project = require('./project'), Project.init(params) // Project is now an instance of ProjectClient 

我也有一些数据对象types的类: Entity (一个标准的parsingJSON有效载荷对象)和User (包含用户属性的特殊types的实体)。

ProjectClient类有几个允许RESTful API调用发生的方法,例如Project.GET()Project.PUT() 。 实例化Project “singleton”时,这些工作很好。

我现在正在试图创build一个方便的方法附加到Entity ,将利用ProjectClient的RESTful操作,例如Entity.save()Entity.refresh()

当我尝试将Project导入Entity

 var Project = require('../project') 

我得到:

 TypeError: The super constructor to `inherits` must have a prototype. at Object.exports.inherits (util.js:756:11) 

故障排除导致我与用户中的util.inherits(ProjectUser, ProjectEntity)相关,因为如果我注释掉它,我会得到它:

 Uncaught TypeError: ProjectEntity is not a function 

inherits是怎么回事? 为什么它认为Entity没有原型? 我最好的猜测是,这与我recursion嵌套在其他模块(坏,我知道)的事实有关,但我甚至尝试在各个类中做这样的事情,但无济于事:

 module.exports = _.assign(module.exports, **ClassNameHere**) 

以下是每个类的简化代码:

实体

 var Project = require('../Project'), _ = require('lodash') var ProjectEntity = function(obj) { var self = this _.assign(self, obj) Object.defineProperty(self, 'isUser', { get: function() { return (self.type.toLowerCase() === 'user') } }) return self } module.exports = ProjectEntity 

用户(实体的子类)

 var ProjectEntity = require('./entity'), util = require('util'), _ = require('lodash') var ProjectUser = function(obj) { if (!ok(obj).has('email') && !ok(obj).has('username')) { // This is not a user entity throw new Error('"email" or "username" property is required when initializing a ProjectUser object') } var self = this _.assign(self, ProjectEntity.call(self, obj)) return self } util.inherits(ProjectUser, ProjectEntity) module.exports = ProjectUser 

项目(“单身”,但不是真的)

 'use strict' var ProjectClient = require('./lib/client') var Project = { init: function(options) { var self = this if (self.isInitialized) { return self } Object.setPrototypeOf(Project, new ProjectClient(options)) ProjectClient.call(self) self.isInitialized = true } } module.exports = Project 

客户

 var ProjectUser = require('./user'), _ = require('lodash') var ProjectClient = function(options) { var self = this // some stuff happens here to check options and init with default values return self } ProjectClient.prototype = { GET: function() { return function() { // async GET request with callback } }, PUT: function() { return function() { // async PUT request with callback } } } module.exports = ProjectClient 

所以,正如你正确的扣除循环依赖的问题。 您的Entity模块需要Project模块,该模块需要Client模块,该Client模块需要需要Entity模块的User模块。

你可以做些什么,但这取决于你的出发点。 如果您首先需要Project模块,那么它应该与所提供的代码一起工作,因为Entity模块不会对Project模块执行任何操作。 该模块上没有任何输出,所以它只是一个空的对象。 然后再次,该模块上的任何错误源将与导出的对象依赖于该模块内的任何相关。 所以如果你需要在Entity使用init的对象,那么会出现问题。

你可以在初始化依赖链之前导出一些方法/函数,这会使它们可用。 以NodeJS文档为例:

a.js

 console.log('a starting'); exports.done = false; var 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; var a = require('./a.js'); console.log('in b, a.done = %j', a.done); exports.done = true; console.log('b done'); 

main.js

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

所以, main.js是一个起点。 它需要a.js立即导出一个done属性,然后需要b.js b.js也出口一个done财产。 下一行需要a.js ,它不会再次加载a.js ,但返回到目前为止导出的属性(包括done属性)。 在这一点上, a是不完整的,但它已经设法给b足够的继续工作。 它的下一行(在b.js )将打印导出的属性(a.done,这是false),然后将导出的属性重置为true。 我们回到require('b.js')require('b.js') 现在b.js已经完全加载了,其余的很容易解释。

输出是:

 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 

这里是一个例子,以防万一你想阅读官方文档。

对,所以问题是…你能做什么?

在初始化依赖周期之前,可以导出一些东西,只要你不需要这些依赖。 例如,您可以:

a.js

 exports.func = function(){ console.log('Hello world'); } var b = require('./b.js'); console.log('a done'); 

b.js

 var a = require('./a.js'); a.func(); //i'm still incomplete but i got func! console.log('b done'); 

你不能:

a.js

 b.func(); //b isn't even an object yet. var b = require('./b.js'); console.log('a done'); 

b.js

 exports.func = function(){ console.log('hello world'); } var a = require('./a.js'); a.func(); console.log('b done'); 

但是,如果你的a.js模块只输出函数,那么只要这些函数没有被其他地方调用,就不存在真正的问题:

a.js

 exports.func = function(){ b.func(); } var b = require('./b.js'); console.log('a done'); 

b.js

 exports.func = function(){ console.log('hello world'); } var a = require('./a.js'); console.log('b done'); 

你正在输出一个使用b的函数,只有当它被调用的时候,函数不需要知道b 。 所以这两个模块正在正确加载。 如果您只是导出函数,那么随后声明依赖关系就没有问题。

所以你可以从你的要点a.jsfunc将正确的工作,因为b参考点现在完整的b.js模块。 只要您在加载依赖关系时不使用导出的函数,就可以遵循此模式。