简体   繁体   English

复杂的循环节点模块依赖性抛出“TypeError:超级构造函数'继承'必须有一个原型”

[英]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: 这是每个类的一些简化代码:

Entity 实体

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

User (a subclass of Entity) 用户(实体的子类)

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

Project ("singleton" but not really) 项目(“单身”但不是真的)

'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

Client 客户

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.jsrequire('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.

相关问题 类型错误:超级表达式必须为空或反应本机中的函数_inherits - TypeError: Super expression must either be null or a function _inherits in react native 类型错误:超级表达式必须为空或函数 _inherits - TypeError: Super expression must either be null or a function _inherits 原型和TypeError:someFunction不是构造函数 - Prototype and TypeError: someFunction is not a constructor react-leaflet-heatmap-layer 抛出错误:TypeError:超级表达式必须是 null 或 function,未定义 - react-leaflet-heatmap-layer throwing error: TypeError: Super expression must either be null or a function, not undefined ES6 super()的构造函数和原型方法 - ES6 super() in constructor and prototype methods Angular 2共享模块循环依赖 - Angular 2 shared module circular dependency 原型继承:在子“类”构造函数中调用超级构造函数 - Prototype inheritance: Calling super constructor in sub “class” constructor function 是否可以有一个从Boolean.prototype继承的自定义布尔对象? - Is it possible to have a custom boolean object that inherits from Boolean.prototype? TypeError:超级表达式必须为null或函数 - TypeError: Super expression must either be null or a function (节点:12308)警告:在循环依赖中访问模块导出的不存在属性“MongoError” - (node:12308) Warning: Accessing non-existent property 'MongoError' of module exports inside circular dependency
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM