【问题标题】:'TypeError: undefined is not a function' when jumping 'tween modules跳转“补间模块”时出现“类型错误:未定义不是函数”
【发布时间】:2015-10-25 08:02:00
【问题描述】:

我在 Node 中不断遇到这个问题,每当我相互调用函数时,我的应用程序就会崩溃。

我已经制作了这个最小的工作示例(按照它的方式工作给了我错误):

启动模块

var module2 = require('./module2');

var data = 'data';

module2.doStuff(data);

模块2

var module3 = require('./module3');

function doStuff(data){
    // Stuff happens to 'data'
    module3.takeStuff(data);
}

function doSomethingElse(data){
    console.log(data);
}


module.exports = {
    doStuff: doStuff,
    doSomethingElse: doSomethingElse
};

模块3

var module2 = require('./module2');

function takeStuff(data){
    // Stuff happens to 'data'
    module2.doSomethingElse(data); // I get the type error here
}

module.exports = {
    takeStuff: takeStuff
};

我得到的错误是:

module2.doSomethingElse(data); // I get the type error here
        ^
TypeError: undefined is not a function

start module 调用module2 中的函数,最终调用module3 中的函数,而后者又调用module2 中的函数。

所有模块都是正确需要的,它在module2 中找到第一个方法就好了。

这里发生了什么?当需要从自带的模块中获取功能时,如何处理这种模式?

编辑

调试显示该模块存在,但除了它拥有的原型之外它是空的。我的问题是为什么?在 Node/JavaScript 内部,这里发生了什么?

【问题讨论】:

    标签: javascript node.js typeerror


    【解决方案1】:

    这里的这个问题可以很容易地解决,同时保持应用程序的结构(这很好,就循环引用而言)。

    您只需要保留系统为您的模块提供的默认exports 对象。不要用module.exports = {...}更改它。

    以下应该有效:

    启动模块

    var module2 = require('./module2');
    
    var data = 'data';
    
    module2.doStuff(data);
    

    模块 2

    var module3 = require('./module3');
    
    exports.doStuff = function doStuff(data){
        // Stuff happens to 'data'
        module3.takeStuff(data);
    };
    
    exports.doSomethingElse = function doSomethingElse(data){
        console.log(data);
    };
    

    模块 3

    var module2 = require('./module2');
    
    exports.takeStuff = function takeStuff(data){
        // Stuff happens to 'data'
        module2.doSomethingElse(data); // I get the type error here
    };
    

    说明

    我将尝试从您的起点的第一行开始解释发生了什么:

    1. 在 start.js 中,您需要 module2,但它尚未加载:执行来自 module2.js 的代码
    2. 在 module2.js 中,您需要 module3,但它尚未加载:执行来自 module3.js 的代码
    3. 在 module3.js 中,您需要 module2,并且它已经加载:module2 变量现在包含来自 module2 的导出对象。
    4. 模块 3 的其余部分已执行,您使用 module.exports = {...} 更改其导出对象
    5. 在 module2.js 中,module3 变量现在包含来自 module3 的导出对象。
    6. module2.js 的其余部分被执行,你用module.exports = {...} 改变它的exports 对象
    7. 在 start.js 中,module2 变量现在包含来自 module2 的导出对象。

    这里的问题在第 3 点和第 6 点之间。在您更改对它的引用 (6) 之前,模块 3 正在接收模块 2 (3) 的导出。 使用exports.method = ... 解决了这个问题,因为导出对象永远不会改变。

    【讨论】:

    • exports.functionToExport 和我做的有什么区别?
    • @JohnSnow 我用试探性的解释更新了我的答案
    • 但是当我调试的时候,它进入module2并执行该代码就好了,怎么会是module2没有加载呢?
    • 已加载,但为时已晚。当执行来自 module2 的代码时,module3 已经拥有对其旧(和空)导出对象的引用。
    【解决方案2】:

    你有一个循环引用,模块 2 依赖于模块 3,模块 3 依赖于模块 2,模块 2 依赖于模块 3(依此类推)。因此,在尝试为module 2 解析module 3 时,它无法完成它,因为需要先解析module 3 本身。

    来自https://nodejs.org/api/modules.html#modules_cycles

    当有循环 require() 调用时,模块可能没有 返回时执行完毕。

    为了防止死循环,一个未完成的a.js副本 export 对象返回给 b.js 模块。

    所以该模块可用,但不完整(这说明您只收到一个原型)。无论如何,循环引用是一种代码味道,尽量避免它:)

    但是,如果您将模块 2 和 3 作为依赖项放在起始模块中,它应该仍然可以工作。

    var module2 = require('./module2');
    var module3 = require('./module3');
    
    var data = 'data';
    
    module2.doStuff(data);
    

    【讨论】:

    • "当有循环 require() 调用时,模块可能在返回时尚未完成执行。"那么你有它。 facepalms 现在有一个 SO question 提到它。
    • @JohnSnow 是的,我会把这句话放在我的回答中以澄清:)
    • @JohnSnow 如果节点抛出某种循环引用警告会很好。很高兴为您提供帮助!
    • 也许是一张想要的票?
    • 你在开始的第三个模块中提到的最新添加对我不起作用。
    猜你喜欢
    • 2015-02-25
    • 1970-01-01
    • 2020-06-27
    • 2014-05-26
    • 2014-10-12
    • 2019-02-05
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    相关资源
    最近更新 更多