复杂的循环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.js
和func
将正确的工作,因为b
参考点现在完整的b.js
模块。 只要您在加载依赖关系时不使用导出的函数,就可以遵循此模式。