javascript - Märkligt node.js-runtimefel: XXX är inte en funktion (Men jag definierar faktiskt XXX som en funktion~!)
Jag har hittat en speciell kombination/sekvens av JavaScript-kod som orsakar konstiga runtime-fel i Node.js ("XXX är inte en funktion", men jag definierar faktiskt XXX som en funktion!). Det beror inte på några andra bibliotek eller verktyg, men vissa enkla TypeScript/JavaScript-koder kan orsaka detta fel. Jag kan inte förstå varför jag fick ett sådant runtime-fel, kan någon hjälpa mig? Om det motsvarar några kända buggar i Node.js, vänligen ange motsvarande ärendenummer så att jag kan veta mer om detta. Tack~!
(Jag har redan uppgraderat allt till den senaste versionen: node.js v4.1.1, Visual Studio 2015 communityversion, VS 2015 Node.js-verktyg 1.1.30716.01)
Faktum är att jag använder Visual Studio 2015 communityversion för att skriva några TypeScript-koder (.ts) och få JavaScript-koderna (.js) genererade av VS automatiskt. Mot den speciella kodningssekvensen kunde Visual Studio 2015 glatt acceptera den och generera motsvarande JavaScript-koder. Men när node.js kör dessa JavaScript-koder, skulle node.js generera det märkliga körtidsfelet. Därför tror jag att detta beteende helt och hållet är relaterat till node.js och inte till TypeScript.
Sammanfattningsvis anser typescript-modulen i VS2015 att denna kombination är en giltig typescript/javascript-sekvens, men de genererade javascript-filerna kunde inte köras av node.js, och om jag bara ändrar lite på ordningen av satserna, fungerar det~! Verkligen konstigt.
Typescript/javascript-kodningskombinationerna/seven innehåller 8 enkla filer. Jag tillbringade några dagar med att begränsa problemet och upptäckte att kombinationen är den enklaste formen och inte kan förenklas ytterligare för att få samma runtime-fel. Om jag tar bort några delar ytterligare försvinner runtime-felet. När jag kör filen "main.js" genererar node.js följande fel, men jag kan inte förstå varför. A_function1() och A_function2() är 2 enkla funktioner (Du kan se hela filinnehållet nedan), och node.js kan framgångsrikt hitta och köra A_function1(), men inte 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...
========================================================================= De 8 typescript-filerna är följande:
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();
}
========================================================================= De motsvarande javascriptfiler som genererats av VS2015 är följande:
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;
Svar #1
basarats svar är korrekt i att du har en cyklisk referens, men det svaret är inte riktigt fullständigt för din fråga (även om det, för att vara rättvis, vad du frågar om egentligen inte är särskilt speciellt alls; cykliska modulberoenden är ganska vanliga i vissa kodstrukturer).
I din kod exporteras modul a på ett annat sätt än resten av dina moduler. I modul a exporterar du ett separat värde för modulen (export=A), medan dina andra moduler helt enkelt lägger till funktioner till deras standardexportobjekt (export function C_function). Att modul a försöker exportera ett separat värde är nyckeln till problemet.
När ett cykliskt modulberoende uppstår i Node.js, måste Node.js för att bryta cykeln definiera det exporterade värdet av den konfliktande modulen som ett standardexportobjekt som den skapar, även om du försökte specificera ett explicit exportvärde. För moduler som helt enkelt lägger till egenskaper till standardexportobjektet (export function och export var) är detta inte ett problem. Men när du försöker definiera ett annat exportobjekt (med export =) kommer det objektet slutligen att kasseras till förmån för standardexportobjektet.
Med andra ord, export=A ignoreras i din kod eftersom den modulen slutar vara en del av en cykel som denna:
main -> d -> f -> b -> a -> g -> c -> a
När a ses för andra gången har körningen inget annat val än att ge c ett standardexportobjekt, eftersom den annars inte kan lösa beroende grafen.
För att åtgärda ditt anrop behöver du antingen omstrukturera din kod för att ta bort det cykliska beroendet, eller så behöver du exportera funktioner och variabler som läggs till på standardexportobjektet, istället för att försöka exportera ett annat objekt som du själv har skapat (class A).
Svar #2
Cykelreferenser
Om du har en cyklisk referens: https://nodejs.org/api/modules.html#modules_cycles kan det hända att du får tillbaka ett objekt som kanske inte har en funktion kopplad till modulimporten.
Prova https://github.com/TypeStrong/atom-typescript#dependency-view, det kommer att hitta alla cykliska referenser du kan ha 🌹
