我正在尝试找出我在代码中遇到的错误。我写了一个简单的例子,它使用类a,b和条目文件c复制了bug。
我的代码在哪里出错?
修改
许多答案都与我的代码结构有关!我想澄清所需的功能。
a:需要b的静态实例。因此,A需要B。
b函数(无静态):仅接受instanceof a
的对象。因此,B需要A。
具体来说,我有一个"短语" (a)类和" Parser" (b)类。解析器只接受短语。短语使用其静态解析器实例在创建时解析自己。
a.js
'use strict';
let b = require('./b.js');
class a {
constructor() {
}
}
new b();
module.exports = a;
b.js
'use strict';
let a = require('./a.js');
class b {
constructor() {
}
}
module.exports = b;
c.js
'use strict';
let b = require('./b.js');
new b();
运行: node c.js
错误:
C:\code\a.js:11
new b();
^
TypeError: b is not a function
at Object.<anonymous> (C:\code\projects\elegance\data-frog\tag\datastructures\a.js:11:7)
at Module._compile (module.js:413:34)
at Object.Module._extensions..js (module.js:422:10)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Module.require (module.js:367:17)
at require (internal/module.js:16:19)
at Object.<anonymous> (C:\code\projects\elegance\data-frog\tag\datastructures\b.js:3:9)
at Module._compile (module.js:413:34)
at Object.Module._extensions..js (module.js:422:10)
答案 0 :(得分:2)
您是从另一个内部递归调用每个模块,因此当您在a模块中导入b时,它还没有准备好。
你不应该在b模块中要求a.js.
如果出于某种原因需要这样做,请重构代码,因为这是一个反模式。
您可以使用第三个需要a和b的模块:
const A = require('a'); //Export the class, not the instance
const B = require('b'); //Export the class, not the instance
var a = new A();
var b = new B();
a.b = b;
这样您就可以确定a和b是各自类的实例,或者如果您愿意,仍然可以使用instanceof
所需的类。
另外,如果我可以建议,请为您的类使用大写命名。
答案 1 :(得分:2)
根据您的编辑,我的直接建议是,如果您选择保留它,则继续使用class Phrase
,特别是因为您希望能够记录对象的类型...
...但是要将该构造函数用作其他语言中的struct
。
你的Parser
可以知道Phrase
,因为它解析了它们,但是解析器接受鸭子类型的条目真的有害,因为灵活性的差异很大,如果你检查您的方法想要的接口(实际使用的结构上的字段),而不是检查严格的实例。
一个简单的示例可能是从Web服务加载短语列表。在解析它们之前,你真的需要将每一行实例化为一个短语吗?
这是一个如何处理你有一个低级结构的情况的例子,它不需要DI的工厂(因为它是如此低级别),而是可以直接导入到Parser
模块。
// phrase.js
class Phrase {
constructor (content = "") {
this.content = content;
}
static from (phrase) {
return new Phrase(phrase.content);
}
static isPhrase (phrase) {
return phrase && phrase.content !== null && phrase.content !== undefined;
}
}
export default Phrase; // or module.exports
// parser.js
import Parser from "./parser"; // or require, whatever floats your boat
class Parser {
static parse ( input = "" ) {
const preparedContent = Phrase.isPhrase( input )
? Parser.prepareContent( input.content )
: Parser.prepareContent( input );
return new Phrase(content);
}
static prepareContent ( input = "" ) {
// ...
}
parse (content) {
return Parser.parse(content);
}
}
export default Parser;
我现在可以创建Parser
的实例,或者只是静态使用它
我可以把它原始输入,或者我可以把它换成短语,它会做正确的事情
而且,我不依赖于Parser能够定义Phrase是什么。 Parser依靠Phrase构建器来了解Phrase是什么。
Parser总是返回一个“解析”短语的新实例,但它的输入现在更加轻松。同样,基于即将发布的内容,您现在可以使用您的输入做更多理智的事情(即:提供良好的默认值,例如安全地返回空短语(我的示例世界中的phrase.content
可能是空字符串) ),而不是基于instanceof)返回null
或投掷。
查看我在其他地方提供的答案,了解更多详细信息(并查看问题,看看他在目前的地点与目前的地点完全相同的路障)。
但答案是,你不应该依赖循环依赖,这会对你的代码库造成可怕的,可怕的事情。
如果你真的,真的,真的,真的需要这样做(你没有),你应该使用Factory
来确保构建/使用正确的实例,并将工厂注入你的应用程序,而不是类本身。
如果您严重 依赖,批发,typeof
或instanceof
功能,以确保您的应用程序的坚固性 - 特别是以支票需要通知的方式引用,那么您的应用程序首先不是很坚固
你的课程不需要彼此了解,当你把他们的建筑委托给工厂时,如果这是你选择的路径,那么课程也不应该知道工厂。
答案 2 :(得分:1)
可能会使这更复杂。
查看这个简单的灯类。
class Light {
constructor(state = false) {
this.state = state;
}
on() {
this.state = true;
return this.state;
}
off() {
this.state = false;
return this.state;
}
}
const myLight = new Light();
myLight.on()
您可以看到状态是在构造函数中定义的。
希望这有帮助。