函数范围内未定义的Javascript全局变量。

时间:2021-04-28 16:49:22

I am new to JavaScript and trying to make a simple node server. Here is my code:

我是JavaScript新手,尝试创建一个简单的节点服务器。这是我的代码:

var activeGames = {}

exports.initialize = function(){
    var gameID = "game12345"
    activeGames.gameID = new Game(gameID, "player1", "player2")
}

I call the initialize function from another module, and I get an error stating that activeGames is undefined. activeGames is at the outermost scope of the file. I tried adding 'this' before activeGames.gameID but that did not fix it. Why is activeGames undefined? Thanks in advance.

我从另一个模块调用initialize函数,我得到一个错误,说明activeGames没有定义。activeGames位于文件的最外层。我试着在activeGames之前添加“this”。但这并不能解决问题。为什么activeGames定义?提前谢谢。

EDIT: Here's how I'm calling this code.

编辑:这是我如何调用这个代码。

In my base index file I have

在我的基本索引文件中

const handler = require("./request-handler.js")
handler.initialize()

In request-handler.js, I have

在请求处理程序。js,我

var gameManager = require('./game-manager')

exports.initialize = function(){
    gameManager.initialize()
}

3 个解决方案

#1


1  

JavaScript has lexical scope, not dynamic scope. ref: https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping

JavaScript有词法范围,而不是动态范围。裁判:https://en.wikipedia.org/wiki/Scope_(computer_science)# Lexical_scoping

Lexical scope means that whether a variable is accessible or not depends on where they appear in the source text, it doesn't depend on runtime information.

词法作用域意味着变量是否可访问取决于它们在源文本中的位置,它不依赖于运行时信息。

example:

例子:

function foo() {
  var bar = 42;
  baz();
}

function baz() {
  console.log(bar); // error because bar is not in the scope of baz
}

the same problem happens in your code,

同样的问题也发生在你的代码中,

var activeGames

var activeGames

is not in scope.

不在范围内。

try this variation:

试试这种变化:

exports.initialize = function(){
    var activeGames = {}
    var gameID = "game12345"
    activeGames.gameID = new Game(gameID, "player1", "player2")
}

A good solution could be to use a class and export it: --THIS CODE IS NOT TESTED--

一个好的解决方案可能是使用一个类并将其导出:—此代码没有经过测试—

class gamesManager {
   var activeGames = {}

   initialize() {
      var gameID = "game12345"
      activeGames.gameID = new Game(gameID, "player1", "player2")
   }
}

exports.gamesManager = gamesManager

USE:

使用:

const activeGames = require('./game-manager');
const activeGamesInstance = new activeGames.gamesManager();
activeGamesInstance.initialize();

#2


1  

Need a code sample for this one. I ran this locally and it worked fine, although your code has a big issue which may be a part of your problem. It looks like you want to keep track of multiple games in activeGames. You need to use this syntax instead:

为此需要一个代码示例。我在本地运行这个程序,它运行得很好,尽管您的代码有一个很大的问题,这可能是问题的一部分。看起来你想在活动游戏中跟踪多个游戏。你需要使用这种语法来代替:

activeGames[gameID] = new Game(gameID, "player1", "player2")

Here's my working code:

这是我的工作代码:

index.js:

index.js:

const handler = require("./request-handler");

handler.initialize('game-1234');
handler.initialize('game-5678');

request-handler.js:

request-handler.js:

var gameManager = require('./game-manager');

exports.initialize = function(gameID) {
    gameManager.initialize(gameID);
}

game-manager.js:

game-manager.js:

var activeGames = {};

class Game {
    constructor(id, player1, player2) {
        this.id = id;
        this.player1 = player1;
        this.player2 = player2;
    }
}

exports.initialize = function(gameID) {
    activeGames[gameID] = new Game(gameID, "player1", "player2");

    console.log(`game initialized! ${ Object.keys(activeGames).length } active games`);
}

Running node index results in this:

运行的节点索引会导致以下结果:

game initialized! 1 active games
game initialized! 2 active games

#3


1  

When you require a script file in Node.js, it is compiled as part of a function called with named parameters require, module, exports and other exposed variables as arguments1. Variables declared at file level within the required script become function level variables in the enclosing module wrapper and retained inside its closure.

当您需要节点中的脚本文件时。它作为函数的一部分进行编译,函数的命名参数require、模块、导出和其他公开的变量arguments1。在所需脚本的文件级声明的变量在封装模块包装器中成为函数级变量,并保留在其闭包中。

Hence your "global variable" is no such thing: it's a variable defined inside a closure...

因此,您的“全局变量”不是这样的:它是在闭包中定义的变量……

An important question then is does the module loader make variables declared in a parent module available to scripts required inside the parent. A quick test shows that the answer is general: no, modules do not have automatic access to variables declared in other modules - those variables are inside closures.

一个重要的问题是,模块装入器是否使父模块中声明的变量对父模块中所需的脚本可用。快速测试表明,答案是一般的:不,模块没有自动访问在其他模块中声明的变量——这些变量在闭包中。

This indicates that to pass variable values to scripts that have been required, generally pass them as argument values to exported functions.

这表明要将变量值传递给已经需要的脚本,通常要将它们作为参数值传递给导出的函数。

It is also possible to export any javascript value as a property of module.exports from within a required script, or add properties to an exports object after it has been returned from requiring a script. Hence it is technically feasible to pass information up and down between modules by adding properties to exports objects.

也可以将任何javascript值作为模块的属性导出。从所需的脚本中导出,或者在导出对象从需要脚本返回后向该对象添加属性。因此,通过向导出对象添加属性在模块之间传递信息在技术上是可行的。

Redesigned code has multiple options to

重新设计的代码有多种选择

  1. define activeGames at the application level and pass it down as a parameter to modules needing access to it, or

    在应用程序级别定义activeGames,并将其作为参数传递给需要访问它的模块,或者

  2. export activeGames from game-manager.js by adding

    出口从game-manager activeGames。js通过添加

      exports.activeGames = activeGames
    

    to the end of the file. This will not take care of exporting activeGames out of the parent module request-manager.js for use elsewhere, but it could be a start. Or

    直到文件的末尾。这将不考虑从父模块request-manager导出activeGames。可以在其他地方使用js,但这可能是一个开始。或

  3. define activeGames as a global variable (in node) using

    将activeGames定义为一个全局变量(在节点中)

      global.activeGames = {}  // define a global object
    

    Defining global variables is not encouraged as it can lead to collisions (and consequent program failure) between names used by applications, code libraries, future library updates and future versions of ECMAScript. Or,

    不鼓励定义全局变量,因为它可能导致应用程序、代码库、未来库更新和未来版本的ECMAScript之间名称之间的冲突(以及随后的程序失败)。或者,

  4. Define an application namespace object for data global to the application. Require it wherever access to application data is needed:

    为应用程序定义数据全局的应用程序命名空间对象。要求它在任何需要访问应用程序数据的地方:

    • create appdata.js as an empty file. Optionally include a comment:

      创建appdata。js作为一个空文件。包括一个注释(可选):

       // this files exports module.exports without modification
      
    • require appdata.js wherever needed.

      需要appdata。任何需要的js。

      var appData = require('./appdata.js')
      appData.gameData = {}; // for example
      

    This relies on node.js maintaining a cache of previously required modules and does not recompile modules simply because they have been required a second time. Instead it returns the exports object of the previous require.

    这依赖于节点。js维护以前需要的模块的缓存,不重新编译模块,仅仅是因为它们需要第二次编译。相反,它返回先前需要的export对象。

Happy festive season.

快乐的节日期间。


References
1The Node.js Way - How require() Actually Works

#1


1  

JavaScript has lexical scope, not dynamic scope. ref: https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping

JavaScript有词法范围,而不是动态范围。裁判:https://en.wikipedia.org/wiki/Scope_(computer_science)# Lexical_scoping

Lexical scope means that whether a variable is accessible or not depends on where they appear in the source text, it doesn't depend on runtime information.

词法作用域意味着变量是否可访问取决于它们在源文本中的位置,它不依赖于运行时信息。

example:

例子:

function foo() {
  var bar = 42;
  baz();
}

function baz() {
  console.log(bar); // error because bar is not in the scope of baz
}

the same problem happens in your code,

同样的问题也发生在你的代码中,

var activeGames

var activeGames

is not in scope.

不在范围内。

try this variation:

试试这种变化:

exports.initialize = function(){
    var activeGames = {}
    var gameID = "game12345"
    activeGames.gameID = new Game(gameID, "player1", "player2")
}

A good solution could be to use a class and export it: --THIS CODE IS NOT TESTED--

一个好的解决方案可能是使用一个类并将其导出:—此代码没有经过测试—

class gamesManager {
   var activeGames = {}

   initialize() {
      var gameID = "game12345"
      activeGames.gameID = new Game(gameID, "player1", "player2")
   }
}

exports.gamesManager = gamesManager

USE:

使用:

const activeGames = require('./game-manager');
const activeGamesInstance = new activeGames.gamesManager();
activeGamesInstance.initialize();

#2


1  

Need a code sample for this one. I ran this locally and it worked fine, although your code has a big issue which may be a part of your problem. It looks like you want to keep track of multiple games in activeGames. You need to use this syntax instead:

为此需要一个代码示例。我在本地运行这个程序,它运行得很好,尽管您的代码有一个很大的问题,这可能是问题的一部分。看起来你想在活动游戏中跟踪多个游戏。你需要使用这种语法来代替:

activeGames[gameID] = new Game(gameID, "player1", "player2")

Here's my working code:

这是我的工作代码:

index.js:

index.js:

const handler = require("./request-handler");

handler.initialize('game-1234');
handler.initialize('game-5678');

request-handler.js:

request-handler.js:

var gameManager = require('./game-manager');

exports.initialize = function(gameID) {
    gameManager.initialize(gameID);
}

game-manager.js:

game-manager.js:

var activeGames = {};

class Game {
    constructor(id, player1, player2) {
        this.id = id;
        this.player1 = player1;
        this.player2 = player2;
    }
}

exports.initialize = function(gameID) {
    activeGames[gameID] = new Game(gameID, "player1", "player2");

    console.log(`game initialized! ${ Object.keys(activeGames).length } active games`);
}

Running node index results in this:

运行的节点索引会导致以下结果:

game initialized! 1 active games
game initialized! 2 active games

#3


1  

When you require a script file in Node.js, it is compiled as part of a function called with named parameters require, module, exports and other exposed variables as arguments1. Variables declared at file level within the required script become function level variables in the enclosing module wrapper and retained inside its closure.

当您需要节点中的脚本文件时。它作为函数的一部分进行编译,函数的命名参数require、模块、导出和其他公开的变量arguments1。在所需脚本的文件级声明的变量在封装模块包装器中成为函数级变量,并保留在其闭包中。

Hence your "global variable" is no such thing: it's a variable defined inside a closure...

因此,您的“全局变量”不是这样的:它是在闭包中定义的变量……

An important question then is does the module loader make variables declared in a parent module available to scripts required inside the parent. A quick test shows that the answer is general: no, modules do not have automatic access to variables declared in other modules - those variables are inside closures.

一个重要的问题是,模块装入器是否使父模块中声明的变量对父模块中所需的脚本可用。快速测试表明,答案是一般的:不,模块没有自动访问在其他模块中声明的变量——这些变量在闭包中。

This indicates that to pass variable values to scripts that have been required, generally pass them as argument values to exported functions.

这表明要将变量值传递给已经需要的脚本,通常要将它们作为参数值传递给导出的函数。

It is also possible to export any javascript value as a property of module.exports from within a required script, or add properties to an exports object after it has been returned from requiring a script. Hence it is technically feasible to pass information up and down between modules by adding properties to exports objects.

也可以将任何javascript值作为模块的属性导出。从所需的脚本中导出,或者在导出对象从需要脚本返回后向该对象添加属性。因此,通过向导出对象添加属性在模块之间传递信息在技术上是可行的。

Redesigned code has multiple options to

重新设计的代码有多种选择

  1. define activeGames at the application level and pass it down as a parameter to modules needing access to it, or

    在应用程序级别定义activeGames,并将其作为参数传递给需要访问它的模块,或者

  2. export activeGames from game-manager.js by adding

    出口从game-manager activeGames。js通过添加

      exports.activeGames = activeGames
    

    to the end of the file. This will not take care of exporting activeGames out of the parent module request-manager.js for use elsewhere, but it could be a start. Or

    直到文件的末尾。这将不考虑从父模块request-manager导出activeGames。可以在其他地方使用js,但这可能是一个开始。或

  3. define activeGames as a global variable (in node) using

    将activeGames定义为一个全局变量(在节点中)

      global.activeGames = {}  // define a global object
    

    Defining global variables is not encouraged as it can lead to collisions (and consequent program failure) between names used by applications, code libraries, future library updates and future versions of ECMAScript. Or,

    不鼓励定义全局变量,因为它可能导致应用程序、代码库、未来库更新和未来版本的ECMAScript之间名称之间的冲突(以及随后的程序失败)。或者,

  4. Define an application namespace object for data global to the application. Require it wherever access to application data is needed:

    为应用程序定义数据全局的应用程序命名空间对象。要求它在任何需要访问应用程序数据的地方:

    • create appdata.js as an empty file. Optionally include a comment:

      创建appdata。js作为一个空文件。包括一个注释(可选):

       // this files exports module.exports without modification
      
    • require appdata.js wherever needed.

      需要appdata。任何需要的js。

      var appData = require('./appdata.js')
      appData.gameData = {}; // for example
      

    This relies on node.js maintaining a cache of previously required modules and does not recompile modules simply because they have been required a second time. Instead it returns the exports object of the previous require.

    这依赖于节点。js维护以前需要的模块的缓存,不重新编译模块,仅仅是因为它们需要第二次编译。相反,它返回先前需要的export对象。

Happy festive season.

快乐的节日期间。


References
1The Node.js Way - How require() Actually Works