首页 > 解决方案 > 当两个文件相互需要时,Nodejs“不是构造函数”错误

问题描述

我需要将两个 js 对象(播放器和房间)放入彼此的文件中。但是当我这样做时,发生了意外错误。

应用程序.js:

const Player = require("./Player").Player

let player = new Player()

播放器.js:

const Room = require ("./Room").Room
let room = new Room()

const Player = function () {
    let room = new Room()
}

exports.Player = Player

房间.js:

const Player = require("./Player").Player
let player = new Player()

const Room = function () {
    
}

exports.Room = Room

和错误:

/home/mosi/Github/test/Room.js:2
let player = new Player()
             ^

TypeError: Player is not a constructor
    at Object.<anonymous> (/home/mosi/Github/test/Room.js:2:14)
    at Module._compile (internal/modules/cjs/loader.js:778:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Module.require (internal/modules/cjs/loader.js:692:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at Object.<anonymous> (/home/mosi/Github/test/Player.js:1:14)
    at Module._compile (internal/modules/cjs/loader.js:778:30)

标签: javascriptnode.jsobjectconstructor

解决方案


因为它们相互依赖,所以你的模块是循环的;请参阅此处的 Node.js 文档。正如他们所说,“需要仔细规划才能使循环模块依赖项在应用程序中正常工作。” :-)

如果您可以避免在一个循环中使用模块,那通常是最好的。

使用 ESM,您所要做的“全部”就是不在PlayerorRoom的顶层使用Player.jsor Room.js,但是对于您正在使用的 CJS 模块,您还必须做更多的事情。我不是在 CJS 模块中整理循环的专家,但我认为您需要做的主要事情是不要尝试立即获取PlayerRoom导出。让模块先完成加载。例如:

app.js

const Player = require("./Player").Player;

let player = new Player();

Player.js

// Import the module exports object, but don't grab its `Room` property yet
const RoomMod = require ("./Room");
// Don't do this at the top level: let room = new Room()

const Player = function () {
    // Now it's safe to use the `Room` property
    let room = new RoomMod.Room();
};

exports.Player = Player;

Room.js

// Get the module exports object, but don't try to get the `Player` property yet
const PlayerMod = require("./Player");
// Don't do this at the top level: let player = new Player()

const Room = function () {
    
};

exports.Room = Room;

对于它的价值,如果您使用 ESM(JavaScript 标准模块),您不必只导入模块,然后再使用属性,因为使用 ESM,导入的绑定是实时的。因此,尽管您仍然必须避免在 and 的顶层使用and ,但Player您可以在不需要模块命名空间对象(CJS 导出对象的 ESM 等效项)的情况下导入它们:RoomRoom.jsPlayer.js

app.js

import { Player } from "./Player.js";

let player = new Player();

Player.js

import { Room } from "./Room.js";

export const Player = function () {
    let room = new Room()
};

Room.js

import { Player } from "./Player.js";

export const Room = function () {
    
};

推荐阅读