【问题标题】:ReferenceError: Cannot access 'Player' before initializationReferenceError:初始化前无法访问“播放器”
【发布时间】:2020-02-08 00:08:48
【问题描述】:

所以我一直在通过 ESM 模块加载器在 Nodejs 上使用带有导入/导出的 ES6 样式语法。在我开始收到与导入有关的错误之前,一切都很好。

以下是错误消息:

joseph@InsaneMachine:~/placeholder2/main-server$ npm start

> main-server@1.0.0 start /home/joseph/placeholder2/main-server
> nodemon --experimental-modules src/index.mjs

[nodemon] 1.19.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node --experimental-modules src/index.mjs`
(node:16942) ExperimentalWarning: The ESM module loader is experimental.
file:///home/joseph/placeholder2/main-server/src/games/game-player.mjs:3
export default class GamePlayer extends Player
                                        ^

ReferenceError: Cannot access 'Player' before initialization
    at file:///home/joseph/placeholder2/main-server/src/games/game-player.mjs:3:41
    at ModuleJob.run (internal/modules/esm/module_job.js:109:37)
    at async Loader.import (internal/modules/esm/loader.js:132:24)
[nodemon] app crashed - waiting for file changes before starting...

这里是文件播放器(基类):

import PasswordHash from 'password-hash';

import GamesService from '../games/games.service.mjs';

import PlayersService from './players.service.mjs';

import QueueingService from '../queueing/queueing.service.mjs';

export default class Player
{
    constructor(object)
    {
        Object.assign(this, JSON.parse(JSON.stringify(object)));
    }

    get id()
    {
        return this._id.toString();
    }

    equals(other)
    {
        if(other.id != null)
            return other.id == this.id;
        return false;
    }

    checkPassword(password)
    {
        return PasswordHash.verify(password, this.password);
    }

    online()
    {
        return PlayersService.consumer.isPlayerOnline(this);
    }

    inQueue()
    {
        return QueueingService.queued(this);
    }

    inGame()
    {
        return GamesService.getActiveGameByPlayer(this) != null;
    }

    reduce()
    {
        return {
            id: this.id,
            username: this.username,
            email: this.email,
            admin: this.admin,
            online: this.online(),
            in_queue: this.inQueue(),
            in_game: this.inGame(),
        };
    }

    static hashPassword(password)
    {
        return PasswordHash.generate(password);
    }

    static schema = {
        username: String,
        password: String,
        email: String,
        email_confirmed: Boolean,
        admin: Boolean,
    }
}

还有 GamePlayer(子类):

import Player from '../players/player.mjs';

export default class GamePlayer extends Player
{
    constructor(player, token)
    {
        super(player);
        this.token = token;
    }
}

以及项目的层次结构:

src/
 -- games/
 --  -- game-player.mjs
 --  -- ...
    players/
 --  -- player.mjs
 --  -- ...
 -- ...

我该如何解决这个导入问题,除非这是其他问题?

编辑:据我所知,我没有使用 Babel,我使用的是 Node.js 提供的 --external-modules。不确定它是如何工作的。

【问题讨论】:

  • ReferenceError 表示Player 在尝试使用之前未声明。 game-player.mjs 正在尝试扩展在被解释时不存在的东西。
  • 但是我怎么才能让它在之前被声明呢? @zfrisch

标签: javascript node.js es6-modules


【解决方案1】:

我去了 Node.JS 论坛并询问可能是什么问题。根本不是 babel 问题,只是循环依赖。例如:

// A.js
import B from './B.js'
export default class A{}
// B.js
import A from './A.js'
export default class B extends A{}

抱歉,没有足够的信息来解决这个问题。我在 node.js github 上得到了很多帮助,有人浏览了我在 github 上的项目,最终找到了一个实例,其中两个模块相互指向。

【讨论】:

  • 那你是怎么解决的?有importOnce 功能吗?
  • 我不得不重新构建代码,以便没有这些循环依赖。
  • 我有一个项目,它到处都有这样的“循环”导入,它一直运行良好,直到我刚刚添加的一个与其他几乎相同。其他人实际上仍然工作正常。我的理解是import 会“自动”解决这些问题。我还没有发现问题,但我认为在使用类名时还有其他原因导致它无法正常工作。
【解决方案2】:

imports 中的依赖关系可能太难解决,所以它放弃了,在需要定义 GamePlayer 时,Player 未初始化。

正如我在另一个答案的评论中提到的,import 可以以“循环”方式使用,但 Node.js 不能总是解开依赖关系。

在我的情况下,一个文件中的类和另一个文件中的子类没有问题,它们都是import,很难确切地说它在哪里变得太复杂了,但这是我所拥有的简化版本:

// server.js
import Acorn from './acorn.js';
import Branch from './branch.js';
class Server {
    ...
}

// universe.js
import Acorn from './acorn.js';
import Branch from './branch.js';
import Thing from './thing.js';
export default class Universe {
    things(type) {
       if (Thing.klass[type]) {
           ...
       }
    }
    ...
}

// acorn.js
import Thing from './thing.js';
export default class Acorn extends Thing {
    ...
}

// branch.js
import Thing from './thing.js';
export default class Branch extends Thing {
    ...
}

// thing.js
import Acorn from './acorn.js';
import Branch from './branch.js';
export default class Thing {
    static klass(type) {
        const klass = {acorn: Acorn, branch: Branch};
        ...
        return klass;
    }

    constructor(...) {
        this.type = this.constructor.name.toLowerCase();
        ...
    }
    
    ...
}

我对浏览器和服务器端 Node.js 使用相同的代码,因此我还使用 Babel 进行了转译,它可以很好地处理一切。但是 Node 可能还有其他限制,使其更加困难,因为至少就导入而言,它与浏览器(和其他)处于不同的轨道上,现在正试图弥合差距。而且这个结可能比肉眼看起来更复杂。

最后我放弃了我的模式中最循环的部分,这是我在 Thing 本身中引用 Thing 子类的部分。我将其提取为“类工厂”模式,工厂知道 Thing 子类,但 Thing 及其子类不需要工厂。

【讨论】:

    【解决方案3】:

    从您的 Babel 配置中删除 es2015。 类扩展原生 ES6 类和 Babel 转译为 ES,这很可能导致问题

    【讨论】:

    • 对不起,我不知道去哪里从 Babel 中删除 es2015。那是我必须在 package.json 中编辑的东西吗
    • 另外我根本没有使用 Babel,我正在使用 --experimental-modules
    猜你喜欢
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-12
    • 2020-12-29
    • 2021-09-10
    相关资源
    最近更新 更多