我有一个复杂的Node SDK项目,它使用一些类继承来尝试和static-ify Javascript.我正在使用Node的模块缓存行为为SDK(Project
该类,共享实例ProjectClient
)创建类似单身的行为.对于init,它看起来像这样:
var Project = require('./project'), Project.init(params) // Project is now an instance of ProjectClient
我也有一些数据的对象类型类:( Entity
标准解析的JSON有效负载对象)和User
(包含用户属性的特殊类型的实体).
本ProjectClient
类有几种方法,让REST的API调用发生,例如Project.GET()
,Project.PUT()
.在实例化Project
"单例" 时,这些工作正常.
我现在正在尝试创建附加的方便方法,例如,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)
in 相关User
,因为如果我将其评论出来,我会得到这个:
Uncaught TypeError: ProjectEntity is not a function
发生了什么事inherits
?为什么它认为Entity
没有原型?我最好的猜测是,它与我在其他模块中递归嵌套模块这一事实有关(糟糕,我知道),但我甚至尝试在各种类中执行类似的操作,但无济于事:
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
MinusFour.. 5
因此,正确地推断出循环依赖性存在问题.您的Entity
模块需要Project
模块,该Client
模块需要User
模块,该Entity
模块需要模块,该模块需要模块.
你可以做些什么,但这取决于你的出发点.如果您Project
首先需要该模块,那么它应该使用提供的代码,因为Entity
模块不对模块执行任何操作Project
.在该模块上没有导出任何内容,因此它只是一个空对象.然后,该模块上的任何错误源都将与该模块中所依赖的任何导出对象相关.因此,如果您需要init
内部对象,Entity
则会出现问题.
您可以在初始化依赖关系链之前导出一些方法/函数,这将使它们可用.以NodeJS
docs 为例:
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),然后将导出的属性重置done
为true.我们重新回到a.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
模块的参考点.只要在加载依赖项时不使用导出的函数,就可以遵循此模式.
因此,正确地推断出循环依赖性存在问题.您的Entity
模块需要Project
模块,该Client
模块需要User
模块,该Entity
模块需要模块,该模块需要模块.
你可以做些什么,但这取决于你的出发点.如果您Project
首先需要该模块,那么它应该使用提供的代码,因为Entity
模块不对模块执行任何操作Project
.在该模块上没有导出任何内容,因此它只是一个空对象.然后,该模块上的任何错误源都将与该模块中所依赖的任何导出对象相关.因此,如果您需要init
内部对象,Entity
则会出现问题.
您可以在初始化依赖关系链之前导出一些方法/函数,这将使它们可用.以NodeJS
docs 为例:
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),然后将导出的属性重置done
为true.我们重新回到a.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
模块的参考点.只要在加载依赖项时不使用导出的函数,就可以遵循此模式.