【问题标题】:require and module.exports : TypeError: X is not a functionrequire 和 module.exports : TypeError: X is not a function
【发布时间】:2019-08-22 17:43:06
【问题描述】:

文件结构:

server
├── controllers
│   ├── locationsController.js
│   ├── mainController.js
│   └── utilisateursController.js
├── models
│   ├── Locations.js
│   ├── Utilisateurs.js
|   └── ...
├── routes.js
└── server.js

在mainController 我有一个函数isValid 来检查一个字符串,我把它放在那里是因为我想从utilisateursController.js 和locationsController.js 访问它。我将其导出如下:

mainController.js

const lc = require('./locationsController');
const uc = require('./utilisateursController');

// ...

module.exports = {
  // Associate all models
  associateAll: function () {
    lc.associateLocations();
    uc.associateUtilisateurs();
  },
  isValid: function(...fields) {
    for (let i = 0; i < fields.length; i++)
      if (fields[i] === undefined || fields[i] === null || fields[i] === '')
        return false;
    return true;
  }
};

问题

我可以从utilisateursController.js 访问函数isValid,但是当我尝试从locationsController.js 执行相同操作时,出现此错误:

(node:6461) UnhandledPromiseRejectionWarning: TypeError: mc.isValid is not a function
    at exports.getAllTasks (.../server/controllers/locationsController.js:30:11)

代码

utilisateursController.js

从这个文件,我可以完美访问isValid,没有错误。

const mc = require('./mainController');

// ...

exports.login = async function (req, res) {

  let response = {
    // ...
  }
  if (req.query == null) {
    response.infoMsg = 'Query empty...';
    res.send(response);
    return;
  }

  const usernameInput = req.query.username;
  const passwordInput = req.query.password;

  if (!mc.isValid(usernameInput, passwordInput)) {
    response.infoMsg = 'username or password is empty...'
    res.send(response);
    return;
  }

  // ...

}

locationsController.js

从这个文件中,我得到了上面提到的错误,我真的不知道为什么......

const mc = require('./mainController');

// ...

exports.getAllTasks = async function (req, res) {
  let response = {
    // ...
  }

  const usernameInput = req.params.username;

  if (!mc.isValid(usernameInput)) {
    response.infoMsg = 'No parameters given...';
    res.send(response);
    return;
  }

  // ...

}

我的想法

我认为这可能是因为需求的决议的顺序......

调试器说什么

utilisateursController.js

locationsController.js

我真的不知道是什么导致了这个问题......

【问题讨论】:

  • 请阅读帮助的minimal reproducible example 部分。完成创建一个最小但完整的示例的过程是调试的重要部分,并且(如果你不明白的话)提出一个问题。

标签: javascript node.js export require


【解决方案1】:

问题是因为mainController、utilisateursController 和locationsController 之间存在循环关系。 utilisateursController 和 locationsController 都需要 mainController,mainContoller 需要 utilisateursController 和 locationsController。因此,Node.js 的 CommonsJS 样式的模块解析最终会在您的至少一个(可能两个)模块中运行顶级代码,并使用占位符对象来导出其他模块之一。 (显然,在您的情况下,locationsController 获得了 mainController 导出的占位符。utilisateursController 也可能这样做,但不会尝试在顶层使用它。)

如果您避免在顶层使用mc,仅在稍后调用的函数中使用它,该占位符将在您需要它之前被填充,一切都会好起来的。您引用的代码似乎只在函数中使用mc,但考虑到您遇到的错误,显然您的真实代码并非如此。

更多内容请关注Node.js modules documentation's "Cycles" section。


旁注:原生 JavaScript 模块不会发生这种情况(通常称为“ESM”,用于“ECMAScript Modules ),因为即使存在循环依赖,它们也会在顶级模块代码运行之前解决。

【讨论】:

  • 谢谢!我将isValid 放入帮助文件以避免循环关系
  • @davalres - 听起来很完美,不错! :-)
  • 另一个问题,如果我将isValid 函数中的条件替换为return !!fields[i],会产生完全相同的效果吗?我正在探索 JS 中的真/假概念(因为 undefined、null 和 '' 的真值是 false,对吗?)。
  • @DavidAlvarez - 不,这会有所不同,原因有两个: 1. 您只返回第一个字段的标志,因为return 终止了函数。 2. 有几个不在您的列表中的值是虚假的,例如0 和NaN。您的原始检查专门针对三个虚假值(undefined、null 和 ""),但完整列表为 undefined、null、""、0、NaN、当然,false。 (所有其他值都是真实的。)
  • 感谢您帮助我理解这个概念!
猜你喜欢
  • 2018-10-25
  • 2016-01-05
  • 2017-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-20
  • 2017-01-04
相关资源
最近更新 更多