Javascript global variable undefined in function scope - javascript

I am new to JavaScript and trying to make a simple node server. Here is my code:
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.
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
var gameManager = require('./game-manager')
exports.initialize = function(){
gameManager.initialize()
}

JavaScript has lexical scope, not dynamic scope.
ref: 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
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();

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:
const handler = require("./request-handler");
handler.initialize('game-1234');
handler.initialize('game-5678');
request-handler.js:
var gameManager = require('./game-manager');
exports.initialize = function(gameID) {
gameManager.initialize(gameID);
}
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

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.
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.
Redesigned code has multiple options to
define activeGames at the application level and pass it down as a parameter to modules needing access to it, or
export activeGames from game-manager.js by adding
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
define activeGames as a global variable (in node) using
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,
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:
// this files exports module.exports without modification
require appdata.js wherever needed.
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.
Happy festive season.
References
1The Node.js Way - How require() Actually Works

Related

How to share scope and variables in Node?

I'm writing a node program and I want several functions contained in separate files to access and modify the same scope of variables without defining them in the global scope.
The solution I found is using a module to share its scope but it seems a bit tricky.
Here's the file tree :
- index.js
- file-a.js
- file-b.js
- shared-scope.js
index.js :
require('./file-a')
require('./file-b')
file-a.js :
const sharedScope = require('./shared-scope');
sharedScope.foo = 'bar'
file-b.js :
const sharedScope = require('./shared-scope');
console.log(sharedScope) // Prints { foo: 'bar' }
shared-scope.js :
module.exports = {};
What do you think about it? Is this a good way of sharing a scope between modules?
Sure, that's called a singleton. Some might say that any global, shared state is bad, but if that's what you want, this is a perfectly fine and simple way to do it.
The most obvious alternative is to define the data in one place and then in other modules, define functions which work on the data.
file-b.js:
module.exports = function (data) {
data.foo = "bar";
}
index.js:
const assignFoo = require('./file-b.js');
const data = {};
assignFoo(data);

How to add value to global object from module in Node.js

I'm trying to add some value to the global or local object, but nothin happens.
I'm tried create:
global.test = {}; in main.js
And to add value test[name] = value in second js
tried create var test = {}; in second.js
And to add value test[name] = value in second js
But this examples doesn't helped me.
Code in my files:
main.js:
global.common = require('./second.js');
global.test = {}
second.js:
module.exports = {
main: function (name, value) {
test[name] = value;
}
};
second.js invoked in another files, but the whole point is displayed in the code above.
Reading the NodeJS docs:
In browsers, the top-level scope is the global scope. This means that within the browser var something will define a new global variable. In Node.js this is different. The top-level scope is not the global scope; var something inside a Node.js module will be local to that module.
Seems like you are expecting the global scope of NodeJS to work the same way that JS works in browsers but that is not the case.
See this question for more information : What is the 'global' object in NodeJS

Creating Node.JS Module

I have been creating software in NodeJS for years, but I have rarely ever looked into the module side of it, since I've never needed to do that stuff myself.
I have created a test.js file, which is my module, all it contains is this (i've tried this.host, self.host, var host ... nothing is working)
var tests = function () {
this.host = "http://127.0.0.1/";
};
exports = tests;
In my server.js I try and use it and I can never get host to actually output
var tests = require('./test.js');
console.log(tests);
console.log(tests.host);
I always get this output, saying that tests variable has no properties ... which I set in the module.
sudo node server.js
{}
undefined
The host variable as you defined it in the tests function is not accessible through tests's prototype.
This means that in order to access it, you should be creating a new instance of tests, using the new operator :
var Tests = require('./tests');
var instance = new Tests();
// Now you can access `instance.host`
Also, as David said, use module.exports to export your function.
Don't do exports = tests. Either do exports.tests = tests or module.exports = tests.
Basically, you have to first decide if you want your module to just have properties that can be directly accessed or if you want it to have a constructor function that creates an object when it is called that then has properties or it could even just be a regular function that you call that returns a value. You have mixed and matched the first two schemes (pieces of each) and thus it does not work. I will show you both schemes:
Here's the scheme where your module exports a constructor function from which you can create an object (when you new it):
// test.js module
var tests = function () {
this.host = "http://127.0.0.1/";
};
module.exports = tests;
// main module server.js
var Tests = require('./test.js');
var t = new Tests();
console.log(t.host);
And, here's the scheme where you just directly export properties:
// test.js module
module.exports = {
host: "http://127.0.0.1/"
};
// main module server.js
var tests = require('./test.js');
console.log(tests);
console.log(tests.host);
Keep in mind that whatever you assign to module.exports is what require() will return after it loads your module. So, in your first case, you're assigning a function that is intended to be a constructor function so you have to use it as a constructor function in order for it to work properly.
In my second example, I assign an object to module.exports so you can then treat it just like an object after loading the module with require(). That means you can then just directly access its properties as you would for an object.
console.log(tests()); will work if the you add return statement inside the function.

meaning of module.exports= function in node.js

I am taking first steps with node.js and obviously one of the first things i tried to do was exporting some data from a module, so i tried this simple case:
dummy.js:
var user = "rally";
module.exports = {
user:user
};
and than required it from a different file like this:
var dummy = require('./dummy.js');
console.log(dummy.user); // rally
So far so good, every thing works, but now i dived into code where there is this definition in the beginning of the module:
module.exports = function(passport,config, mongoose) {}
and i don't understand whats the meaning of it and how can i work with it.
just for trying to understand i defined some variables inside this abstract function but couldn't get their value from any other file.
any idea how can i export variables from module defined like this..? so for example i could require this module and get the "Dummy" variable and use it in a different file
module.exports = function(passport,config, mongoose) {
var dummy = "Dummy";
}
It works exactly the same as the first one does, only that it exports a function instead of an object.
The module that imports the module can then call that function:
var dummy = require('./dummy.js');
dummy();
any idea how can i export variables from module defined like this..?
Since functions are just objects, you can also assign properties to it:
module.exports = function(passport,config, mongoose) {}
module.exports.user = 'rally';
However I'd argue that this is less expected if a module directly exports a function. You are probably better off exporting the function as its own export:
exports.login = function(passport,config, mongoose) {}
exports.user = 'rally';
WHAT IS A MODULE?
A module encapsulates related code into a single unit of code. When creating a module, this can be interpreted as moving all related functions into a file.
// dummy.js
var exports = module.exports = {};
The utility of dummy.js increases when its encapsulated code can be utilized in other files. This is achieved by using exports.
HOW ARE THEY INVOKED?
You could declare your functions outside of the module.exports block. Functions inside exports can be invoked exactly the same way as variables or any other object.
EXAMPLE
//dummy.js
var myVariable = "foo";
var myFunction = function(){
//some logic
};
module.exports{
myVariable : myVariable,
myFunction : myFunction,
myVariableTypeTwo : "bar",
myFunctionTypeTwo : function () {
//some logic
}
}
We can now access the publicly available methods of dummy.js as a property from any js file.
var dummy = require('./dummy.js');
dummy.myVariable; //foo
dummy.myFunction();
dummy.myVariableTypeTwo; //bar
dummy.myFunctionTypeTwo();
NOTE
In the code above, we could have replaced module.exports with exports and achieved the same result. If this seems confusing, remember that exports and module.exports reference the same object.

How can I seperate objects that is under same closure into different files

I have following structure for my client;
var myObject = (function(){
var mainObjectList = [];
var globalObject = {
init:function(mainObjectId){
var logger = {};
var utilityObject1 = {};
var utilityObject2 = {};
var mainObject = {};
mainObjectList.push(mainObject);
},//init
someOtherMethods:function(){}
};//globalObject
return globalObject;
})();
with my client I can say
myObject.init(5);
and create a new structure.
My problem is I have a lot of utility objects inside init function closure (logger, utilityObject1, utilityObject2..). My total file exceeded 1000 lines so I want to separate all utility objects into different files to have a better project. for example I could separate logger, utilityObject1 , utilityObject2 to their own files. the problem is since objects are in closure I cannot just add them to main object in separate files. so I thought of following injection method.
//start of file1
var myObject = (function(){
var mainObjectList = [];
var globalObject = {
init:function(mainObjectId){
var logger;
var utilityObject1 = {};
var utilityObject2 = {};
var mainObject = {};
mainObjectList.push(mainObject);
},//init
someOtherMethods:function(){},
injectLogger:function(creator){
this.logger = creator();
}
};//globalObject
return globalObject;
})();
//start of file2
myObject.injectLogger(function(){return {};});
That way I can separate my files for development. but in production I can concatenate files to have one file. But I have some problems with this design. I just added an accessible injectLogger function into myObject. and my logger cannot use other local variables in closure now(I have to pass them to creator object now). My question is is there any other way to separate that kind of code into files. (maybe an external utility.)
I like to use google's closure compiler http://code.google.com/closure/compiler/
If you don't want to use something like that, you might try this sort of thing: (Make sure you load globalObject.js first to define the variable).
//globalObject.js
var globalObject = function() {
}
//init.js
globalObject.prototype.init = function() {
this.utilityFunction();
//do something with utilityObject1
this.utilityFunction(this.utilityObject1);
}
//utilityFunction.js
globalObject.prototype.utilityFunction= function() {}
//utilityObject1.js
globalObject.prototype.utilityObject1 = {};
//main.js
var myObject = new globalObject();
myObject.init();
You could then overwrite the function by doing something like this:
//main.js
var myObject = new globalObject();
myObject.utilityFunction = function() {
//new function
}
myObject.init(); // This will then use the new utilityFunction method you just set.
As I understand it, you want to have some lexical variables that all of your modules close over, but you want to keep the code for the various modules in different files.
One way to achieve this exact behavior is to create a single Javascript file by concatenating the module definitions together:
Header.js-partial
var myObject = (function(){
var mainObjectList = [];
var globalObject = {
init:function(mainObjectId){
Logger.js:
function Logger() { this.enabled = true; }
Logger.prototype.log = function() {
if (window.console && window.console.log) {
return window.console.log.apply(window.console.log, arguments]);
}
}
etc.
Add other module files as desired. They can reference lexical variables.
Footer.js-partial
}// end init
}; // end globalObject
return globalObject;
})();
In the end you need a script that will concatenate all of these files together into a single js file. There is no other way to get truly lexical variable access in pure Javascript.
A superior alternative
That said, this technique is ripe for confusion and I don't recommend it. Closures are generally meant for closing over local variables, not program-wide variables. If you use a lexical variables declared 1000 lines ago, you will spend some amount of time tracking down where all your variables were declared when running your program. Instead you should probably enclose private, 'global' data in the globalObject. For example, store mainObjectList in globalObject.mainObjectList and reference that in module files.
I recommend a solution like that described by nemophrost. Each file should be valid javascript on its own.
You could use command-line PHP to serve as a preprocessor to combine your files. For example, main.js.php could look like this:
(function() {
<?php require('foo.js.php');?>
})();
and then run the command php main.js.php > combined.js when you want to produce an output file usable for minification and deployment.
This has the advantage that you can load main.js.php onto a server to test a new version of the code without recompiling. You can just put a line at the very beginning of main.js.php to get PHP to send the correct MIME type:
<?php header('Content-type: text/javascript');?>
The way I deal with this problem to check to see if the global object exists and if not create it. This way the order isn't important.
// file 1
var Global_Obj = Global_Obj || {}; // creates empty global object
Global_Obj.An_Obj = { /* stuff */ };
// file 2
var Global_Obj = Global_Obj || {}; // uses created global object
Global_Obj.Another_Obj = { /* stuff */ };

Categories

Resources