Node.js循环require类后变量无法识别,求问题原因解析
循环依赖导致的模块未定义问题分析与解决
你的代码文件
mysqlDAOFactory.js
var mysql = require('mysql'); var mysqlUserDAO = require('./mysqlUserDAO'); var mysqlSessionDAO = require('./mysqlSessionDAO'); class mysqlDAOFactory { static createConnection() { var connection = mysql.createConnection({ host:'localhost', user:'root', password:'', database:'QASys' }); return connection; } static getDbInstance() { return mysql; } getUserDAO() { return new mysqlUserDAO(); } getSessionDAO() { return new mysqlSessionDAO(); } } module.exports = mysqlDAOFactory;
mysqlUserDAO.js
var mysqlDAOFactory = require('./mysqlDAOFactory.js'); var bcrypt = require('bcryptjs'); var preparedStatements = require('./preparedStatements'); class mysqlUserDAO { constructor() { this.connection = mysqlDAOFactory.createConnection(); this.mysql = mysqlDAOFactory.getDbInstance(); } } module.exports = mysqlUserDAO;
test.js
var mysqlDAOFactory = require('./mysqlDAOFactory.js'); var UserDAO = mysqlDAOFactory.getUserDAO();
问题根源:循环依赖(Circular Dependency)
你遇到的这个问题是Node.js模块加载机制下的典型循环依赖问题,具体加载流程是这样的:
- 运行
test.js,首先加载mysqlDAOFactory.js。 - 在
mysqlDAOFactory.js里,会优先加载它依赖的mysqlUserDAO.js。 - 加载
mysqlUserDAO.js时,它又去加载mysqlDAOFactory.js——但此时mysqlDAOFactory.js还没完成整个模块的加载(还没执行到module.exports = mysqlDAOFactory这一步,类的静态方法也还没完全挂载),所以mysqlUserDAO.js里拿到的mysqlDAOFactory是一个未完全初始化的不完整模块。 - 当
mysqlUserDAO的构造函数调用mysqlDAOFactory.createConnection()时,自然找不到这个方法,导致报错。
解决方案
方案一:重构代码,消除循环依赖(推荐)
把两个模块共享的数据库连接逻辑抽成一个独立的模块,让mysqlDAOFactory和mysqlUserDAO都依赖这个新模块,彻底打破循环依赖。
1. 创建独立的dbConnection.js模块
var mysql = require('mysql'); module.exports = { // 抽离原来的createConnection静态方法 createConnection: function() { return mysql.createConnection({ host:'localhost', user:'root', password:'', database:'QASys' }); }, // 抽离原来的getDbInstance静态方法 getDbInstance: function() { return mysql; } };
2. 修改mysqlDAOFactory.js
var dbConnection = require('./dbConnection'); var mysqlUserDAO = require('./mysqlUserDAO'); var mysqlSessionDAO = require('./mysqlSessionDAO'); class mysqlDAOFactory { getUserDAO() { return new mysqlUserDAO(); } getSessionDAO() { return new mysqlSessionDAO(); } } module.exports = mysqlDAOFactory;
3. 修改mysqlUserDAO.js
var dbConnection = require('./dbConnection'); var bcrypt = require('bcryptjs'); var preparedStatements = require('./preparedStatements'); class mysqlUserDAO { constructor() { this.connection = dbConnection.createConnection(); this.mysql = dbConnection.getDbInstance(); } } module.exports = mysqlUserDAO;
这种方式不仅解决了循环依赖问题,还让代码结构更清晰,后续维护也更方便。
方案二:延迟依赖加载(临时解决,不推荐)
如果暂时不想重构代码,可以把mysqlDAOFactory的require放在mysqlUserDAO的构造函数里,延迟加载时机:
var bcrypt = require('bcryptjs'); var preparedStatements = require('./preparedStatements'); class mysqlUserDAO { constructor() { // 不在顶部require,而是在构造函数里加载 var mysqlDAOFactory = require('./mysqlDAOFactory.js'); this.connection = mysqlDAOFactory.createConnection(); this.mysql = mysqlDAOFactory.getDbInstance(); } } module.exports = mysqlUserDAO;
不过这种方法只是绕过了问题,没有从根源解决循环依赖,长期来看会让代码逻辑变得混乱,不建议在生产环境使用。
内容的提问来源于stack exchange,提问作者Brian Phan
相关产品推荐
相关产品推荐

