为何通过require引入的函数在类实例中调用时返回undefined?
问题原因
你遇到的是Node.js的*循环依赖(循环引用)*问题,和类、模块的访问机制无关,也不是语法类低级错误。
你的两个模块形成了互相引用的关系:
Site.js顶层通过require('../DataFunctions.js')加载数据工具模块DataFunctions.js顶层又通过require('./classes/Site.js')加载Site类模块
Node.js处理CommonJS模块循环引用时,会先给正在加载的模块返回未完成的导出对象作为占位,等模块代码全部执行完再补全导出内容。你当前的执行路径是DataFunctions先加载Site,Site加载到一半又回头加载还没执行完导出逻辑的DataFunctions,所以Site里拿到的dataFunctions就是未初始化完成的占位对象,最终表现为undefined。
其他疑问解答
- 只要
Page.js没有和其他模块形成循环依赖,Site类可以正常引用Page类,无任何问题 - Site类的实例本身有权限访问正确加载后的dataFunctions模块,问题出在模块加载顺序而非访问权限
解决方案
方案1:移除不必要的循环依赖(最推荐)
将DataFunctions中依赖Site类的实例创建逻辑抽离到单独的业务文件,从根源消除循环引用:
- 修改
DataFunctions.js,删除顶层的Site引用:
// 删除 let Site = require('./classes/Site.js'); 这行代码 function getSiteInfo(id) { // 原有查询逻辑 } module.exports = { getSiteInfo: function (id) { return getSiteInfo(id); }, };
- 新建
SiteFactory.js统一处理Site实例创建:
let Site = require('./classes/Site.js'); let dataFunctions = require('./DataFunctions.js'); function makeASite() { let id = 2; let site = new Site(id); return site; } module.exports = { makeASite };
方案2:延迟加载模块,避开初始化阶段的循环引用
如果暂时不想调整代码结构,可以将模块引用的时机后移到模块初始化完成之后:
修改Site.js,将dataFunctions的引入放到构造函数内部:
let Page = require('./Page.js'); // 删除顶层的dataFunctions引入代码 module.exports = class Site { constructor(id) { this.id = id; this.pages = []; // 构造函数执行时DataFunctions已经完成初始化,此时引入可以拿到完整的导出对象 const dataFunctions = require('../DataFunctions.js'); console.log(dataFunctions); this.siteInfo = dataFunctions.getSiteInfo(id); } }
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

