[英]Complex circular Node module dependency throwing “TypeError: The super constructor to 'inherits' must have a prototype”
I've got a complex Node SDK project that uses some class inheritance to try and static-ify Javascript. 我有一个复杂的Node SDK项目,它使用一些类继承来尝试和static-ify Javascript。 I'm using Node's module caching behavior to create a singleton-like behavior for the SDK (the
Project
class, a shared instance of ProjectClient
). 我正在使用Node的模块缓存行为为SDK(
Project
类, ProjectClient
的共享实例)创建类似单一的行为。 To init, it looks like this: 对于init,它看起来像这样:
var Project = require('./project'),
Project.init(params)
// Project is now an instance of ProjectClient
I've got some object type classes for data as well: Entity
(a standard parsed JSON payload object) and User
(a special type of Entity that contains user properties). 我也有一些数据的对象类型类:
Entity
(标准解析的JSON有效载荷对象)和User
(包含用户属性的特殊类型的实体)。
The ProjectClient
class has several methods that allow RESTful API calls to occur, eg Project.GET()
, Project.PUT()
. ProjectClient
类有几个允许RESTful API调用发生的方法,例如Project.GET()
, Project.PUT()
。 These work just fine when instantiating the Project
"singleton". 在实例化
Project
“singleton”时,这些工作正常。
I'm now trying to create convenience methods attached to Entity
that will leverage ProjectClient
's RESTful operations, eg Entity.save()
, Entity.refresh()
. 我现在正在尝试创建附加到
Entity
便捷方法,它将利用ProjectClient
的RESTful操作,例如Entity.save()
, Entity.refresh()
。
When I try to import Project
into the Entity
: 当我尝试将
Project
导入Entity
:
var Project = require('../project')
I get: 我明白了:
TypeError: The super constructor to `inherits` must have a prototype.
at Object.exports.inherits (util.js:756:11)
Troubleshooting leads me to this being related to util.inherits(ProjectUser, ProjectEntity)
in User
, because if I comment it out, I get this instead: 故障导致我这之中涉及到
util.inherits(ProjectUser, ProjectEntity)
的User
,因为如果我注释掉,我得到这个:
Uncaught TypeError: ProjectEntity is not a function
What's going on with inherits
? inherits
什么? why does it think that Entity
doesn't have a prototype? 为什么它认为
Entity
没有原型? My best guess is that it's related to the fact that I'm recursively nesting modules within other modules (bad, I know), but I've even tried doing something like this in the various classes but to no avail: 我最好的猜测是,它与我在其他模块中递归嵌套模块这一事实有关(糟糕,我知道),但我甚至尝试过在各种类中做这样的事情,但无济于事:
module.exports = _.assign(module.exports, **ClassNameHere**)
Here's some reduced code for each class: 这是每个类的一些简化代码:
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
So, as you have correctly deducted there's a problem with a circular dependency. 因此,正确地推断出循环依赖性存在问题。 Your
Entity
module requires the Project
Module which requires the Client
module which requires the User
Module which requires the Entity
Module. 您的
Entity
模块需要Project
模块,该模块需要Client
模块,该模块需要需要Entity
模块的User
模块。
There's something you can do about it but it will depend on your starting point. 你可以做些什么,但这取决于你的出发点。 If you require the
Project
module first then it should work with the code provided because the Entity
module doesn't do anything with the Project
module. 如果您首先需要
Project
模块,那么它应该使用提供的代码,因为Entity
模块不对Project
模块执行任何操作。 Nothing has been exported on that module, so it's just an empty object. 在该模块上没有导出任何内容,因此它只是一个空对象。 Then again, any source of errors on that module would be related on whatever exported objects are depended inside that module.
然后,该模块上的任何错误源都将与该模块中所依赖的任何导出对象相关。 So if you'd need the object with
init
inside Entity
then there would be a problem. 因此,如果您需要在
Entity
使用init
的对象,那么就会出现问题。
You can export some methods/functions before initializing the dependency chain, which will make them available by then. 您可以在初始化依赖关系链之前导出一些方法/函数,这将使它们可用。 Take the example of
NodeJS
docs: 以
NodeJS
文档为例:
a.js
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
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
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);
So, main.js
is the starting point. 所以,
main.js
是起点。 It requires a.js
which immediately exports a done
property and then requires b.js
. 它需要
a.js
,它会立即导出一个已done
属性,然后需要b.js
b.js
also exports a done
property. b.js
还出口一个done
财产。 The next line requires a.js
which doesn't load a.js
again but returns the exported properties so far (which includes the done
property). 下一行需要
a.js
,它不会再次加载a.js
但返回到目前为止的导出属性(包括done
属性)。 At this point, a
is incomplete but it has managed to give b
enough to continue working. 在这一点上,
a
是不完整的,但它设法给b
足以继续工作。 It's next line (on b.js
) will print the exported property (a.done, which is false), then reset the exported property done
to true. 它的下一行(上
b.js
)将打印输出特性(a.done,这是假的),然后重新导出属性done
设置为true。 We are back on a.js
in the require('b.js')
line. 我们回到
a.js
的require('b.js')
线。 b.js
is now loaded completely and the rest is pretty easy to explain. b.js
现在完全加载,其余的很容易解释。
Output is: 输出是:
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
Here's the example, just in case you want to read the official docs. 这是一个例子,以防您想阅读官方文档。
Right, so point being... what can you do? 是的,所以重点......你能做什么?
It's possible to export some things before initializing the dependency cycle, just so long as you don't actually need those dependencies. 只要您实际上不需要这些依赖项,就可以在初始化依赖项循环之前导出一些内容。 For example you can:
例如,你可以:
a.js
a.js
exports.func = function(){ console.log('Hello world'); }
var b = require('./b.js');
console.log('a done');
b.js
b.js
var a = require('./a.js');
a.func(); //i'm still incomplete but i got func!
console.log('b done');
You can't: 你不能:
a.js
a.js
b.func(); //b isn't even an object yet.
var b = require('./b.js');
console.log('a done');
b.js
b.js
exports.func = function(){ console.log('hello world'); }
var a = require('./a.js');
a.func();
console.log('b done');
However, if your a.js
module only exports functions then there's no real problem as long as these functions aren't being called somewhere else: 但是,如果你的
a.js
模块只导出函数,那么只要没有在其他地方调用这些函数就没有真正的问题:
a.js
a.js
exports.func = function(){ b.func(); }
var b = require('./b.js');
console.log('a done');
b.js
b.js
exports.func = function(){ console.log('hello world'); }
var a = require('./a.js');
console.log('b done');
You are exporting a function that uses b
, the function doesn't need to know about b
right then, only when it's called. 您正在导出一个使用
b
的函数,该函数不需要知道关于b
,只有当它被调用时。 So both modules are being loaded correctly. 因此两个模块都正确加载。 If you are just exporting functions there's no problem with having the dependencies declared afterwards.
如果您只是导出函数,那么之后声明依赖项就没有问题。
So you can require a.js
from your main point and func
will work correctly as the b
reference points now to the complete b.js
module. 因此,您可以从主要点
a.js
,并且func
将正常工作,因为b
参考点现在指向完整的b.js
模块。 You can follow this pattern so long as you don't use the functions you export when loading the dependencies. 只要在加载依赖项时不使用导出的函数,就可以遵循此模式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.