奇怪的node.js运行时错误:XXX不是函数(但我确实将XXX定义为函数〜!)

时间:2015-10-09 02:29:12

标签: javascript node.js visual-studio typescript

我发现了一个特殊的javascript编码组合/序列会导致奇怪的node.js运行时错误(“XXX不是函数”,但我确实将XXX定义为函数!)。它不依赖于任何其他库和工具,但一些简单的打字稿/ javascript代码可能会导致此错误。我无法理解为什么我会遇到这样的运行时错误,任何人都可以帮助我吗?如果它对应于某个node.js已知错误,请告诉我相应的票号,以便我可以了解更多相关信息。感谢〜!

(我已经升级到最新版本:node.js v4.1.1,Visual Studio 2015社区版,VS 2015 node.js工具1.1.30716.01)

事实上,我正在使用Visual Studio 2015社区版来编写一些打字稿代码( .ts),并自动获取VS生成的javascript代码( .js)。针对这种特殊的编码序列,Visual Studio 2015可以愉快接受它,并生成相应的javascript代码。但是当node.js运行那些javascript代码时,node.js会生成奇怪的运行时错误。因此,我认为这种行为只与node.js完全相关,而不是typescript。

总之,VS2015中的typescript模块认为这个组合是一个有效的打字稿/ javascript序列,但是生成的javascript文件无法由node.js执行,如果我改变了一些语句的顺序,它就可以了〜 !真奇怪。

打字稿/ javascript编码组合/序列包含8个简单文件。我花了几天时间来缩小问题,发现组合是最简单的形式,无法进一步简化以获得相同的运行时错误。如果我进一步删除某些部分,运行时错误将消失。执行“main.js”文件时,node.js会生成以下错误,但我无法理解原因。 A_function1()和A_function2()是2个简单函数(你可以看到下面的完整文件内容),node.js可以成功找到并执行A_function1(),但不能找到和执行A_function2()。

c:\>node --version
v4.1.1

c:\>node main.js
c:\c.js:3
    a.A_function2();
      ^

TypeError: a.A_function2 is not a function
    at Object.C_function (c:\c.js:3:7)
    at Object.<anonymous> (c:\main.js:4:3)
    at Module._compile (module.js:434:26)
    at Object.Module._extensions..js (module.js:452:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Module.runMain [as _onTimeout] (module.js:475:10)
    at Timer.listOnTimeout (timers.js:89:15)
Press any key to continue...

enter image description here

=============================================== ========================== 8个打字稿文件如下:

main.ts

import d = require("./d");    // --> If I change the order of these "imports" to "c --> d --> e", the runtime error would disappear.
import e = require("./e");
import c = require("./c");

c.C_function();

d;
e;

console.log("safe!");   // --> If the execution reaches this line, every thing would be fine. If that runtime error happened, you could not see this "safe!"

a.ts

import g = require("./g");

class A {
    static A_function1() {
    }

    static A_function2() {
        var tmp = g.G_enum.G_enum_value_1;
    }
}

export = A;

b.ts:

import a = require("./a");

export function B_function() {
    a.A_function1();   // --> This line would not cause any runtime error, node.js could successfully find what A_function1() is, and execute it~!
}

c.ts:

import a = require("./a");

export function C_function() {
    a.A_function2();   // --> This line causes the runtime error, but A_function2() is really a function~!
}

d.ts:

import f = require("./f");

f.F_function;

e.ts:

import g = require("./g");

g.G_function;

f.ts:

import b = require("./b");

export function F_function() {
    b.B_function();
}

g.ts:

import c = require("./c");

export enum G_enum {
    G_enum_value_1
}

export function G_function() {
    c.C_function();
}

=============================================== ========================== VS2015生成的相应javascript文件如下:

main.js:

var d = require("./d");
var e = require("./e");
var c = require("./c");
c.C_function();
d;
e;
console.log("safe!");

a.js:

var g = require("./g");
var A = (function () {
    function A() {
    }
    A.A_function1 = function () {
    };
    A.A_function2 = function () {
        var tmp = g.G_enum.G_enum_value_1;
    };
    return A;
})();
module.exports = A;

b.js:

var a = require("./a");
function B_function() {
    a.A_function1();
}
exports.B_function = B_function;

c.js:

var a = require("./a");
function C_function() {
    a.A_function2();
}
exports.C_function = C_function;

d.js:

var f = require("./f");
f.F_function;

e.js:

var g = require("./g");
g.G_function;

f.js:

var b = require("./b");
function F_function() {
    b.B_function();
}
exports.F_function = F_function;

g.js:

var c = require("./c");
(function (G_enum) {
    G_enum[G_enum["G_enum_value_1"] = 0] = "G_enum_value_1";
})(exports.G_enum || (exports.G_enum = {}));
var G_enum = exports.G_enum;
function G_function() {
    c.C_function();
}
exports.G_function = G_function;

2 个答案:

答案 0 :(得分:3)

basarat的答案是正确的,因为你有一个循环引用,但你的问题答案并不完全(尽管,公平地说,你所询问的事实上并不是很特别;循环模块依赖性是公平的在某些代码结构中很常见。)

在您的代码中,模块a的导出方式与其他模块的导出方式不同。在模块a中,导出模块的不同值(export = A),而其他模块只是将函数添加到其默认导出对象(export function C_function)。模块a尝试导出不同值的事实是问题的关键。

当Node.js中出现循环模块依赖关系时,为了打破循环,Node.js必须将冲突模块的导出值定义为 it 创建的默认导出对象,甚至如果您尝试指定显式导出值。对于只是将属性添加到默认导出对象(export functionexport var)的模块,这不是问题。但是,当您尝试定义不同的导出对象(使用export =)时,该对象将最终被丢弃,以支持默认导出对象。

换句话说,代码中会忽略export = A,因为该模块最终会成为这样一个循环的一部分:

主要 - &gt; d - &gt; f - &gt; b - &gt; a - &gt; g - &gt; c - &gt;的

第二次看到a时,运行时别无选择,只能给c一个默认的导出对象,否则它无法解析依赖图。

要修复您的调用,您需要重新构建代码以删除循环依赖项,或者需要导出附加到默认导出对象的函数和变量,而不是尝试导出您创建的其他对象你自己(class A)。

答案 1 :(得分:2)

循环引用

如果您有循环引用:https://nodejs.org/api/modules.html#modules_cycles,可能会返回一个对象,该对象现在可能没有与模块导入关联的功能。

https://github.com/TypeStrong/atom-typescript#dependency-view一个去,它会找到你可能有的任何循环引用